diff --git a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt
index 990e5521979bb227f8f611b31699d6a4768ca349..920911ec3be3d160311679c341dbae119de781ca 100644
--- a/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt
+++ b/app/src/test/java/de/kuschku/quasseldroid/ExampleUnitTest.kt
@@ -1,6 +1,7 @@
 package de.kuschku.quasseldroid
 
-import de.kuschku.libquassel.protocol.connection.ProtocolInfoSerializer
+import de.kuschku.bitflags.of
+import de.kuschku.libquassel.protocol.connection.*
 import de.kuschku.libquassel.protocol.features.FeatureSet
 import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
 import de.kuschku.libquassel.protocol.io.contentToString
@@ -84,9 +85,22 @@ class ExampleUnitTest {
 
       println("Writing protocol")
       write(sizePrefix = false) {
-        UIntSerializer.serialize(it, 0x42b3_3f00u or 0x03u, connectionFeatureSet)
-        IntSerializer.serialize(it, 2, connectionFeatureSet)
-        UIntSerializer.serialize(it, 0x8000_0000u, connectionFeatureSet)
+        ConnectionHeaderSerializer.serialize(
+          it,
+          ConnectionHeader(
+            features = ProtocolFeature.of(
+              ProtocolFeature.Compression,
+              ProtocolFeature.TLS
+            ),
+            versions = listOf(
+              ProtocolMeta(
+                0x0000u,
+                ProtocolVersion.Datastream,
+              ),
+            )
+          ),
+          connectionFeatureSet
+        )
       }
 
       println("Reading protocol")
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ConnectionHeader.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ConnectionHeader.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6e0d939d4dd3320930640d7eb65acf205b33ebcc
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ConnectionHeader.kt
@@ -0,0 +1,25 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.connection
+
+data class ConnectionHeader(
+  val features: ProtocolFeatures,
+  val versions: List<ProtocolMeta>
+)
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ConnectionHeaderSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ConnectionHeaderSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..aea9cf66e2112d61a01910999c2c788c0ff65cbb
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ConnectionHeaderSerializer.kt
@@ -0,0 +1,87 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.connection
+
+import de.kuschku.bitflags.of
+import de.kuschku.bitflags.toBits
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.serializers.primitive.Serializer
+import de.kuschku.libquassel.protocol.serializers.primitive.UByteSerializer
+import de.kuschku.libquassel.protocol.serializers.primitive.UIntSerializer
+import java.nio.ByteBuffer
+
+object ConnectionHeaderSerializer : Serializer<ConnectionHeader> {
+  private const val magic: UInt = 0x42b3_3f00u
+  private const val featureMask: UInt = 0x0000_00ffu
+  private const val lastMagic: UByte = 0x80u
+
+  private fun addMagic(data: UByte): UInt =
+    magic or data.toUInt()
+
+  private fun removeMagic(data: UInt): UByte =
+    (data and featureMask).toUByte()
+
+  private fun <T> writeList(
+    buffer: ChainedByteBuffer,
+    list: List<T>,
+    featureSet: FeatureSet,
+    f: (T) -> Unit
+  ) {
+    for (index in list.indices) {
+      val isLast = index + 1 == list.size
+      val magic = if (isLast) lastMagic else 0x00u
+      UByteSerializer.serialize(buffer, magic, featureSet)
+      f(list[index])
+    }
+  }
+
+  private fun <T> readList(
+    buffer: ByteBuffer,
+    featureSet: FeatureSet,
+    f: () -> T
+  ) : List<T> {
+    val list = mutableListOf<T>()
+    while (true) {
+      val isLast = UByteSerializer.deserialize(buffer, featureSet) != 0x00u.toUByte()
+      list.add(f())
+      if (isLast) {
+        break
+      }
+    }
+    return list
+  }
+
+  override fun serialize(buffer: ChainedByteBuffer, data: ConnectionHeader, featureSet: FeatureSet) {
+    UIntSerializer.serialize(buffer, addMagic(data.features.toBits()), featureSet)
+    writeList(buffer, data.versions, featureSet) {
+      ProtocolMetaSerializer.serialize(buffer, it, featureSet)
+    }
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet) = ConnectionHeader(
+    features = ProtocolFeature.of(
+      removeMagic(UIntSerializer.deserialize(buffer, featureSet))
+    ),
+    versions = readList(buffer, featureSet) {
+      ProtocolMetaSerializer.deserialize(buffer, featureSet)
+    }
+  )
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolFeature.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolFeature.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5ecc2de186cb03d43160ce1101a2881283c1e46b
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolFeature.kt
@@ -0,0 +1,39 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.connection
+
+import de.kuschku.bitflags.Flag
+import de.kuschku.bitflags.Flags
+import de.kuschku.bitflags.toEnumSet
+
+enum class ProtocolFeature(
+  override val value: UByte,
+) : Flag<UByte> {
+  None(0x00u),
+  TLS(0x01u),
+  Compression(0x02u);
+
+  companion object : Flags<UByte, ProtocolFeature> {
+    private val values = values().associateBy(ProtocolFeature::value)
+    override val all: ProtocolFeatures = values.values.toEnumSet()
+  }
+}
+
+typealias ProtocolFeatures = Set<ProtocolFeature>
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfo.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfo.kt
index f71e8185e7249205bebe212f6b7e501277d16258..1ec8299c8aba98274fa379c491bbaa0a33a838b3 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfo.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfo.kt
@@ -20,7 +20,6 @@
 package de.kuschku.libquassel.protocol.connection
 
 data class ProtocolInfo(
-  val flags: UByte,
-  val data: UShort,
-  val version: UByte,
+  val flags: ProtocolFeatures,
+  val meta: ProtocolMeta,
 )
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfoSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfoSerializer.kt
index db699001ac4c963bbf2b958749070583e101d601..2a48500c063268fdfab0217bff75e48d4a229434 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfoSerializer.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolInfoSerializer.kt
@@ -19,24 +19,22 @@
 
 package de.kuschku.libquassel.protocol.connection
 
+import de.kuschku.bitflags.of
+import de.kuschku.bitflags.toBits
 import de.kuschku.libquassel.protocol.features.FeatureSet
 import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.serializers.primitive.Serializer
 import de.kuschku.libquassel.protocol.serializers.primitive.UByteSerializer
-import de.kuschku.libquassel.protocol.serializers.primitive.UShortSerializer
 import java.nio.ByteBuffer
 
-object ProtocolInfoSerializer {
-  fun serialize(buffer: ChainedByteBuffer, data: ProtocolInfo, featureSet: FeatureSet) {
-    UByteSerializer.serialize(buffer, data.flags, featureSet)
-    UShortSerializer.serialize(buffer, data.data, featureSet)
-    UByteSerializer.serialize(buffer, data.version, featureSet)
+object ProtocolInfoSerializer : Serializer<ProtocolInfo> {
+  override fun serialize(buffer: ChainedByteBuffer, data: ProtocolInfo, featureSet: FeatureSet) {
+    UByteSerializer.serialize(buffer, data.flags.toBits(), featureSet)
+    ProtocolMetaSerializer.serialize(buffer, data.meta, featureSet)
   }
 
-  fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): ProtocolInfo {
-    return ProtocolInfo(
-      UByteSerializer.deserialize(buffer, featureSet),
-      UShortSerializer.deserialize(buffer, featureSet),
-      UByteSerializer.deserialize(buffer, featureSet)
-    )
-  }
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet) = ProtocolInfo(
+    ProtocolFeature.of(UByteSerializer.deserialize(buffer, featureSet)),
+    ProtocolMetaSerializer.deserialize(buffer, featureSet)
+  )
 }
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolMeta.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolMeta.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f8c3323d766b943908a68270f12d8becda369987
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolMeta.kt
@@ -0,0 +1,25 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.connection
+
+data class ProtocolMeta(
+  val data: UShort,
+  val version: ProtocolVersion
+)
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolMetaSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolMetaSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..54628ff1c1a21f0055941b4e846a39061468a49d
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolMetaSerializer.kt
@@ -0,0 +1,39 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.connection
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.serializers.primitive.Serializer
+import de.kuschku.libquassel.protocol.serializers.primitive.UByteSerializer
+import de.kuschku.libquassel.protocol.serializers.primitive.UShortSerializer
+import java.nio.ByteBuffer
+
+object ProtocolMetaSerializer : Serializer<ProtocolMeta> {
+  override fun serialize(buffer: ChainedByteBuffer, data: ProtocolMeta, featureSet: FeatureSet) {
+    UShortSerializer.serialize(buffer, data.data, featureSet)
+    UByteSerializer.serialize(buffer, data.version.value, featureSet)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet) = ProtocolMeta(
+    UShortSerializer.deserialize(buffer, featureSet),
+    ProtocolVersion.of(UByteSerializer.deserialize(buffer, featureSet))
+  )
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolVersion.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolVersion.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b4fc9e379af2a4ed2620fb627aaf417b23a89251
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/connection/ProtocolVersion.kt
@@ -0,0 +1,33 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.connection
+
+enum class ProtocolVersion(
+  val value: UByte,
+) {
+  Legacy(0x01u),
+  Datastream(0x02u);
+
+  companion object {
+    private val values = values().associateBy(ProtocolVersion::value)
+    fun of(value: UByte): ProtocolVersion = values[value]
+      ?: throw IllegalArgumentException("Protocol not supported: $value")
+  }
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/NoSerializerForTypeException.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/NoSerializerForTypeException.kt
index 9ce61cef5e5da46faa7a00b01f41c8f6411293c2..b8519291cd378a013b5e2d8cd906f53a22f78f2a 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/NoSerializerForTypeException.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/NoSerializerForTypeException.kt
@@ -31,6 +31,12 @@ sealed class NoSerializerForTypeException : Exception() {
       type: QtType,
       javaType: Class<*>? = null
     ) : this(type.id, javaType)
+
+    override fun toString(): String {
+      return "NoSerializerForTypeException.Qt(type=$type, javaType=$javaType)"
+    }
+
+
   }
 
   data class Quassel(
@@ -48,10 +54,18 @@ sealed class NoSerializerForTypeException : Exception() {
       type: QuasselType,
       javaType: Class<*>? = null
     ) : this(type.qtType, type.typeName, javaType)
+
+    override fun toString(): String {
+      return "NoSerializerForTypeException.Quassel(type=$type, typename=$typename, javaType=$javaType)"
+    }
   }
 
   data class Handshake(
     private val type: String,
     private val javaType: Class<*>? = null
-  ) : NoSerializerForTypeException()
+  ) : NoSerializerForTypeException() {
+    override fun toString(): String {
+      return "NoSerializerForTypeException.Handshake(type='$type', javaType=$javaType)"
+    }
+  }
 }
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/QuasselSerializers.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/QuasselSerializers.kt
index 59c3d014ddd2f5fd17d63ede0474c0ebf50f1f19..ed9ed2883d19fe2a801c12d4b5df8b9b0b1b97fd 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/QuasselSerializers.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/QuasselSerializers.kt
@@ -27,8 +27,8 @@ object QuasselSerializers {
   private val serializers = listOf<QuasselSerializer<*>>(
     BufferIdSerializer,
     BufferInfoSerializer,
-    //DccConfigIpDetectionModeSerializer,
-    //DccConfigPortSelectionModeSerializer,
+    DccIpDetectionModeSerializer,
+    DccPortSelectionModeSerializer,
     //IrcUserSerializer,
     //IrcChannelSerializer,
     //IdentitySerializer,
@@ -38,7 +38,7 @@ object QuasselSerializers {
     NetworkIdSerializer,
     //NetworkInfoSerializer,
     //NetworkServerSerializer,
-    //QHostAddressSerializer,
+    QHostAddressSerializer,
     PeerPtrSerializer,
   ).associateBy(QuasselSerializer<*>::quasselType)
 
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DccIpDetectionModeSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DccIpDetectionModeSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3f79c42b5f725927d5415fd30e4ec70ce828fb4d
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DccIpDetectionModeSerializer.kt
@@ -0,0 +1,50 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.serializers.primitive
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.types.DccIpDetectionMode
+import de.kuschku.libquassel.protocol.types.DccPortSelectionMode
+import de.kuschku.libquassel.protocol.variant.QuasselType
+import java.nio.ByteBuffer
+
+object DccIpDetectionModeSerializer : QuasselSerializer<DccIpDetectionMode?> {
+  override val quasselType: QuasselType = QuasselType.DccConfigIpDetectionMode
+
+  @Suppress("UNCHECKED_CAST")
+  override val javaType: Class<out DccIpDetectionMode?> =
+    DccIpDetectionMode::class.java
+
+  override fun serialize(
+    buffer: ChainedByteBuffer,
+    data: DccIpDetectionMode?,
+    featureSet: FeatureSet
+  ) {
+    UByteSerializer.serialize(buffer, data?.value ?: 0x00u, featureSet)
+  }
+
+  override fun deserialize(
+    buffer: ByteBuffer,
+    featureSet: FeatureSet
+  ): DccIpDetectionMode? {
+    return DccIpDetectionMode.of(UByteSerializer.deserialize(buffer, featureSet))
+  }
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DccPortSelectionModeSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DccPortSelectionModeSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6d1d10796a7d477bd3ca899757636ea75f7e09ae
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DccPortSelectionModeSerializer.kt
@@ -0,0 +1,49 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.serializers.primitive
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.types.DccPortSelectionMode
+import de.kuschku.libquassel.protocol.variant.QuasselType
+import java.nio.ByteBuffer
+
+object DccPortSelectionModeSerializer : QuasselSerializer<DccPortSelectionMode?> {
+  override val quasselType: QuasselType = QuasselType.DccConfigPortSelectionMode
+
+  @Suppress("UNCHECKED_CAST")
+  override val javaType: Class<out DccPortSelectionMode?> =
+    DccPortSelectionMode::class.java
+
+  override fun serialize(
+    buffer: ChainedByteBuffer,
+    data: DccPortSelectionMode?,
+    featureSet: FeatureSet
+  ) {
+    UByteSerializer.serialize(buffer, data?.value ?: 0x00u, featureSet)
+  }
+
+  override fun deserialize(
+    buffer: ByteBuffer,
+    featureSet: FeatureSet
+  ): DccPortSelectionMode? {
+    return DccPortSelectionMode.of(UByteSerializer.deserialize(buffer, featureSet))
+  }
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QHostAddressSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QHostAddressSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..be41946440ae25172b30abf9c9779190fda9093c
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QHostAddressSerializer.kt
@@ -0,0 +1,86 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.serializers.primitive
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.types.NetworkLayerProtocol
+import de.kuschku.libquassel.protocol.variant.QuasselType
+import java.net.Inet4Address
+import java.net.Inet6Address
+import java.net.InetAddress
+import java.nio.ByteBuffer
+
+object QHostAddressSerializer : QuasselSerializer<InetAddress> {
+  override val quasselType: QuasselType = QuasselType.QHostAddress
+  override val javaType: Class<out InetAddress> = InetAddress::class.java
+
+  override fun serialize(
+    buffer: ChainedByteBuffer,
+    data: InetAddress,
+    featureSet: FeatureSet
+  ) {
+    when (data) {
+      is Inet4Address -> {
+        UByteSerializer.serialize(
+          buffer,
+          NetworkLayerProtocol.IPv4Protocol.value,
+          featureSet
+        )
+        buffer.put(data.address)
+      }
+      is Inet6Address -> {
+        UByteSerializer.serialize(
+          buffer,
+          NetworkLayerProtocol.IPv6Protocol.value,
+          featureSet
+        )
+        buffer.put(data.address)
+      }
+      else            -> {
+        UByteSerializer.serialize(
+          buffer,
+          NetworkLayerProtocol.UnknownNetworkLayerProtocol.value,
+          featureSet
+        )
+        throw IllegalArgumentException("Invalid network protocol ${data.javaClass.canonicalName}")
+      }
+    }
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): InetAddress {
+    val type = UByteSerializer.deserialize(buffer, featureSet)
+    return when (NetworkLayerProtocol.of(type)) {
+      NetworkLayerProtocol.IPv4Protocol -> {
+        val buf = ByteArray(4)
+        buffer.get(buf)
+        Inet4Address.getByAddress(buf)
+      }
+      NetworkLayerProtocol.IPv6Protocol -> {
+        val buf = ByteArray(16)
+        buffer.get(buf)
+        Inet6Address.getByAddress(buf)
+      }
+      else                              -> {
+        throw IllegalArgumentException("Invalid network protocol $type")
+      }
+    }
+  }
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QtSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QtSerializer.kt
index dfc7eeccb472e15df824cfeea12d7a14ce2d26bc..3e26d5b562c837512bf328111f697266618ec501 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QtSerializer.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QtSerializer.kt
@@ -19,14 +19,9 @@
 
 package de.kuschku.libquassel.protocol.serializers.primitive
 
-import de.kuschku.libquassel.protocol.features.FeatureSet
-import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
 import de.kuschku.libquassel.protocol.variant.QtType
-import java.nio.ByteBuffer
 
-interface QtSerializer<T> {
+interface QtSerializer<T> : Serializer<T> {
   val qtType: QtType
   val javaType: Class<out T>
-  fun serialize(buffer: ChainedByteBuffer, data: T, featureSet: FeatureSet)
-  fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): T
 }
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..40dd0fc98ca17f33da390adfc911b7a8b0c648e4
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializer.kt
@@ -0,0 +1,29 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.serializers.primitive
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import java.nio.ByteBuffer
+
+interface Serializer<T> {
+  fun serialize(buffer: ChainedByteBuffer, data: T, featureSet: FeatureSet)
+  fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): T
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/DccIpDetectionMode.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/DccIpDetectionMode.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dc6dc77b4202b0c11bdfd17f49b41dfe2ba1d74a
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/DccIpDetectionMode.kt
@@ -0,0 +1,37 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.types
+
+/**
+ * Mode for detecting the outgoing IP
+ */
+enum class DccIpDetectionMode(
+  val value: UByte,
+) {
+  /** Automatic detection (network socket or USERHOST) */
+  Automatic(0x00u),
+  /** Manually specified IP */
+  Manual(0x01u);
+
+  companion object {
+    private val values = values().associateBy(DccIpDetectionMode::value)
+    fun of(value: UByte): DccIpDetectionMode? = values[value]
+  }
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/DccPortSelectionMode.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/DccPortSelectionMode.kt
new file mode 100644
index 0000000000000000000000000000000000000000..705bed66e627ca84713bc46d15cb9e6f977250ae
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/DccPortSelectionMode.kt
@@ -0,0 +1,37 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.types
+
+/**
+ * Mode for selecting the port range for DCC
+ */
+enum class DccPortSelectionMode(
+  val value: UByte,
+) {
+  /** Automatic port selection */
+  Automatic(0x00u),
+  /** Manually specified port range */
+  Manual(0x01u);
+
+  companion object {
+    private val values = values().associateBy(DccPortSelectionMode::value)
+    fun of(value: UByte): DccPortSelectionMode? = values[value]
+  }
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/NetworkLayerProtocol.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/NetworkLayerProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ccae5f617143ddd33b011691b65e7c2ff9fd4930
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/NetworkLayerProtocol.kt
@@ -0,0 +1,34 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.types
+
+enum class NetworkLayerProtocol(
+  val value: UByte,
+) {
+  IPv4Protocol(0x00u),
+  IPv6Protocol(0x01u),
+  AnyIPProtocol(0x02u),
+  UnknownNetworkLayerProtocol(0xFFu);
+
+  companion object {
+    private val values = values().associateBy(NetworkLayerProtocol::value)
+    fun of(value: UByte): NetworkLayerProtocol? = values[value]
+  }
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/connection/ConnectionHeaderSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/connection/ConnectionHeaderSerializerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0aaf86216359c525f915aac66e3bb6d725202ae8
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/connection/ConnectionHeaderSerializerTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.connection
+
+import de.kuschku.bitflags.of
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.serializerTest
+import org.junit.jupiter.api.Test
+
+class ConnectionHeaderSerializerTest {
+  @Test
+  fun testQuasseldroid() = serializerTest(
+    ConnectionHeaderSerializer,
+    ConnectionHeader(
+      features = ProtocolFeature.of(
+        ProtocolFeature.TLS,
+        ProtocolFeature.Compression,
+      ),
+      versions = listOf(
+        ProtocolMeta(
+          data = 0x0000u,
+          version = ProtocolVersion.Datastream,
+        )
+      )
+    ),
+    byteBufferOf(
+      0x42u, 0xb3u, 0x3fu, 0x03u,
+      0x80u, 0x00u, 0x00u, 0x02u
+    )
+  )
+
+  @Test
+  fun testQuasselClient() = serializerTest(
+    ConnectionHeaderSerializer,
+    ConnectionHeader(
+      features = ProtocolFeature.of(
+        ProtocolFeature.TLS,
+        ProtocolFeature.Compression,
+      ),
+      versions = listOf(
+        ProtocolMeta(
+          data = 0x0000u,
+          version = ProtocolVersion.Legacy,
+        ),
+        ProtocolMeta(
+          data = 0x0000u,
+          version = ProtocolVersion.Datastream,
+        )
+      )
+    ),
+    byteBufferOf(
+      0x42u, 0xb3u, 0x3fu, 0x03u,
+      0x00u, 0x00u, 0x00u, 0x01u,
+      0x80u, 0x00u, 0x00u, 0x02u
+    )
+  )
+
+  @Test
+  fun testDebugClient() = serializerTest(
+    ConnectionHeaderSerializer,
+    ConnectionHeader(
+      features = ProtocolFeature.of(      ),
+      versions = listOf(
+        ProtocolMeta(
+          data = 0x0000u,
+          version = ProtocolVersion.Legacy,
+        ),
+        ProtocolMeta(
+          data = 0x0000u,
+          version = ProtocolVersion.Datastream,
+        )
+      )
+    ),
+    byteBufferOf(
+      0x42u, 0xb3u, 0x3fu, 0x00u,
+      0x00u, 0x00u, 0x00u, 0x01u,
+      0x80u, 0x00u, 0x00u, 0x02u
+    )
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DccIpDetectionModeSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DccIpDetectionModeSerializerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..901267d4f1e26fcc8dddb65ac5ed9ded56353967
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DccIpDetectionModeSerializerTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.kuschku.libquassel.protocol.serializers.primitive
+
+import de.kuschku.libquassel.protocol.serializers.QuasselSerializers
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.quasselSerializerTest
+import de.kuschku.libquassel.protocol.types.DccIpDetectionMode
+import de.kuschku.libquassel.protocol.types.DccPortSelectionMode
+import de.kuschku.libquassel.protocol.variant.QuasselType
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+
+class DccIpDetectionModeSerializerTest {
+  @Test
+  fun testIsRegistered() {
+    assertEquals(
+      DccIpDetectionModeSerializer,
+      QuasselSerializers.find<DccIpDetectionMode>(
+        QuasselType.DccConfigIpDetectionMode
+      ),
+    )
+  }
+
+  @Test
+  fun testAutomatic() = quasselSerializerTest(
+    DccIpDetectionModeSerializer,
+    DccIpDetectionMode.Automatic,
+    byteBufferOf(0x00u)
+  )
+
+  @Test
+  fun testManual() = quasselSerializerTest(
+    DccIpDetectionModeSerializer,
+    DccIpDetectionMode.Manual,
+    byteBufferOf(0x01u)
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DccPortSelectionModeSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DccPortSelectionModeSerializerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..aeeca6e97e3445155d278a6639ae5d03c993ed6c
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DccPortSelectionModeSerializerTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.kuschku.libquassel.protocol.serializers.primitive
+
+import de.kuschku.libquassel.protocol.serializers.QuasselSerializers
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.quasselSerializerTest
+import de.kuschku.libquassel.protocol.types.DccIpDetectionMode
+import de.kuschku.libquassel.protocol.types.DccPortSelectionMode
+import de.kuschku.libquassel.protocol.variant.QuasselType
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+
+class DccPortSelectionModeSerializerTest {
+  @Test
+  fun testIsRegistered() {
+    assertEquals(
+      DccPortSelectionModeSerializer,
+      QuasselSerializers.find<DccPortSelectionMode>(
+        QuasselType.DccConfigPortSelectionMode
+      ),
+    )
+  }
+
+  @Test
+  fun testAutomatic() = quasselSerializerTest(
+    DccPortSelectionModeSerializer,
+    DccPortSelectionMode.Automatic,
+    byteBufferOf(0x00u)
+  )
+
+  @Test
+  fun testManual() = quasselSerializerTest(
+    DccPortSelectionModeSerializer,
+    DccPortSelectionMode.Manual,
+    byteBufferOf(0x01u)
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QHostAddressSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QHostAddressSerializerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..08de96efb92b3dbba58ab0b2de97f76ef6dbccc5
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QHostAddressSerializerTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.kuschku.libquassel.protocol.serializers.primitive
+
+import de.kuschku.libquassel.protocol.serializers.QuasselSerializers
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.quasselSerializerTest
+import de.kuschku.libquassel.protocol.variant.QuasselType
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+import java.net.Inet4Address
+import java.net.Inet6Address
+import java.net.InetAddress
+
+class QHostAddressSerializerTest {
+  @Test
+  fun testIsRegistered() {
+    assertEquals(
+      QHostAddressSerializer,
+      QuasselSerializers.find<InetAddress>(
+        QuasselType.QHostAddress
+      ),
+    )
+  }
+
+  @Test
+  fun testIpv4() = quasselSerializerTest(
+    QHostAddressSerializer,
+    Inet4Address.getByAddress(byteArrayOf(
+      127, 0, 0, 1
+    )),
+    byteBufferOf(
+      0x00,
+      127, 0, 0, 1
+    )
+  )
+
+  @Test
+  fun testIpv6() = quasselSerializerTest(
+    QHostAddressSerializer,
+    Inet6Address.getByAddress(ubyteArrayOf(
+      0x26u, 0x07u, 0xf1u, 0x88u, 0x00u, 0x00u, 0x00u, 0x00u, 0xdeu, 0xadu, 0xbeu, 0xefu, 0xcau, 0xfeu, 0xfeu, 0xd1u,
+    ).toByteArray()),
+    byteBufferOf(
+      0x01u,
+      0x26u, 0x07u, 0xf1u, 0x88u, 0x00u, 0x00u, 0x00u, 0x00u, 0xdeu, 0xadu, 0xbeu, 0xefu, 0xcau, 0xfeu, 0xfeu, 0xd1u,
+    )
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt
index 38acc37ea2907735e3b25fd831da02352efbd2c4..aec66f4049da7457ebd7a47b0aef4a2eb21b8026 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt
@@ -22,13 +22,14 @@ import de.kuschku.libquassel.protocol.features.FeatureSet
 import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeSerializer
 import de.kuschku.libquassel.protocol.serializers.primitive.HandshakeMapSerializer
 import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer
+import de.kuschku.libquassel.protocol.serializers.primitive.Serializer
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.jupiter.api.Assertions.assertEquals
 import java.nio.ByteBuffer
 
 fun <T> deserialize(
-  serializer: QtSerializer<T>,
+  serializer: Serializer<T>,
   buffer: ByteBuffer,
   featureSet: FeatureSet = FeatureSet.all()
 ): T {
@@ -38,7 +39,7 @@ fun <T> deserialize(
 }
 
 fun <T> testDeserialize(
-  serializer: QtSerializer<T>,
+  serializer: Serializer<T>,
   matcher: Matcher<in T>,
   buffer: ByteBuffer,
   featureSet: FeatureSet = FeatureSet.all()
@@ -48,7 +49,7 @@ fun <T> testDeserialize(
 }
 
 fun <T> testDeserialize(
-  serializer: QtSerializer<T>,
+  serializer: Serializer<T>,
   data: T,
   buffer: ByteBuffer,
   featureSet: FeatureSet = FeatureSet.all()
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/serialize.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/serialize.kt
index fca9603c923d779cd593ba1dd8842eaff800498a..d389756f23cea6bc0589c9dea36fc3b08cef9a63 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/serialize.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/serialize.kt
@@ -23,6 +23,7 @@ import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
 import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeSerializer
 import de.kuschku.libquassel.protocol.serializers.primitive.HandshakeMapSerializer
 import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer
+import de.kuschku.libquassel.protocol.serializers.primitive.Serializer
 import de.kuschku.libquassel.protocol.testutil.matchers.ByteBufferMatcher
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
@@ -30,7 +31,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
 import java.nio.ByteBuffer
 
 fun <T> serialize(
-  serializer: QtSerializer<T>,
+  serializer: Serializer<T>,
   data: T,
   featureSet: FeatureSet = FeatureSet.all()
 ): ByteBuffer {
@@ -40,7 +41,7 @@ fun <T> serialize(
 }
 
 fun <T> testSerialize(
-  serializer: QtSerializer<T>,
+  serializer: Serializer<T>,
   data: T,
   buffer: ByteBuffer,
   featureSet: FeatureSet = FeatureSet.all()
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/serializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/serializerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e6d4299d885754bfab68fffb27efb5fb20e70562
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/serializerTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.kuschku.libquassel.protocol.testutil
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.serializers.primitive.Serializer
+import org.hamcrest.Matcher
+import java.nio.ByteBuffer
+
+fun <T : Any?> serializerTest(
+  serializer: Serializer<T>,
+  value: T,
+  encoded: ByteBuffer? = null,
+  matcher: ((T) -> Matcher<T>)? = null,
+  deserializeFeatureSet: FeatureSet? = FeatureSet.all(),
+  serializeFeatureSet: FeatureSet? = FeatureSet.all(),
+) {
+  if (encoded != null) {
+    if (deserializeFeatureSet != null) {
+      if (matcher != null) {
+        testDeserialize(serializer, matcher(value), encoded.rewind(), deserializeFeatureSet)
+      } else {
+        testDeserialize(serializer, value, encoded.rewind(), deserializeFeatureSet)
+      }
+    }
+    if (serializeFeatureSet != null) {
+      testSerialize(serializer, value, encoded.rewind(), serializeFeatureSet)
+    }
+  }
+}