SOCKS: Add log_selector support.
authorJeremy Harris <jgh146exb@wizmail.org>
Tue, 8 Dec 2015 22:21:58 +0000 (22:21 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 8 Dec 2015 22:21:58 +0000 (22:21 +0000)
Also make the proxy_* variables meaningful for events associated with proxied transports.

13 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
src/src/deliver.c
src/src/expand.c
src/src/globals.c
src/src/globals.h
src/src/receive.c
src/src/smtp_in.c
src/src/transports/smtp_socks.c
test/confs/4020
test/log/4020
test/scripts/4020-socks/4020
test/stdout/4020

index 6096e1df21eabc8fa118563c3c2cf86869ba29c3..418cb1a11f2d95de5dac6d3b8151cd7a7873445e 100644 (file)
@@ -12038,7 +12038,8 @@ qualified host name. See also &$smtp_active_hostname$&.
        &$proxy_target_address$& &&&
        &$proxy_target_port$& &&&
        &$proxy_session$&
-These variables are only available when built with Proxy Protocol support
+These variables are only available when built with Proxy Protocol
+or Socks5 support
 For details see chapter &<<SECTproxyInbound>>&.
 .wen
 
@@ -35481,7 +35482,7 @@ selection marked by asterisks:
 &` queue_time_overall         `&  time on queue for whole message
 &` pid                        `&  Exim process id
 .new
-&` proxy                      `&  proxy address on <= lines
+&` proxy                      `&  proxy address on <= and => lines
 .wen
 &` received_recipients        `&  recipients on <= lines
 &` received_sender            `&  sender on <= lines
@@ -35615,7 +35616,8 @@ The latter can be disabled by turning off the &%outgoing_interface%& option.
 .cindex "TCP/IP" "logging proxy address"
 &%proxy%&: The internal (closest to the system running Exim) IP address
 of the proxy, tagged by PRX=, on the &"<="& line for a message accepted
-on a proxied connection.
+on a proxied connection
+or the &"=>"& line for a message delivered on a proxied connection..
 See &<<SECTproxyInbound>>& for more information.
 .wen
 .next
@@ -38087,10 +38089,6 @@ Use of a proxy is enabled by setting the &%hosts_proxy%&
 main configuration option to a hostlist; connections from these
 hosts will use Proxy Protocol.
 
-To log the IP of the proxy in the incoming logline, add &"+proxy"&
-to the &%log_selector%& option.
-This will add a component tagged with &"PRX="& to the line.
-
 The following expansion variables are usable
 (&"internal"& and &"external"& here refer to the interfaces
 of the proxy):
@@ -38190,6 +38188,11 @@ The default value for selection bias is 1.
 Proxies from the list are tried according to their priority
 and weight settings until one responds.  The timeout for the
 overall connection applies to the set of proxied attempts.
+
+.section Logging SECTproxyLog
+To log the (local) IP of a proxy in the incoming or delivery logline,
+add &"+proxy"& to the &%log_selector%& option.
+This will add a component tagged with &"PRX="& to the line.
 .wen
 
 . ////////////////////////////////////////////////////////////////////////////
index 1fa19daadc7e78cafe96844cff0604fa79070f49..c167f8392a51ab55a8bdc75fb2ae039be0135c70 100644 (file)
@@ -117,7 +117,8 @@ JH/23 Move SOCKS5 support from Experimental to mainline, enabled for a build
 
 JH/26 Move PROXY support from Experimental to mainline, enabled for a build
       by defining SUPPORT_PROXY.  Note that the proxy_required_hosts option
-      is renamed to hosts_proxy.
+      is renamed to hosts_proxy, and the proxy_{host,target}_{address,port}.
+      variables are renamed to proxy_{local,external}_{address,port}.
 
 
 Exim version 4.86
index 65f148c07b93aadabd62b11d4a37d4303b5f2d33..53712b269ca9cd1cd3ae8c198529fbe1c2aec8e0 100644 (file)
@@ -718,11 +718,24 @@ s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
 if (LOGGING(outgoing_port))
   s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
     addr->host_used->port));
+
+#ifdef SUPPORT_SOCKS
+if (LOGGING(proxy) && proxy_local_address)
+  {
+  s = string_append(s, sizep, ptrp, 3, US" PRX=[", proxy_local_address, US"]");
+  if (LOGGING(outgoing_port))
+    s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
+      proxy_local_port));
+  }
+#endif
+
 return d_log_interface(s, sizep, ptrp);
 }
 
 
 
+
+
 #ifdef SUPPORT_TLS
 static uschar *
 d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
@@ -3297,6 +3310,21 @@ while (!done)
 
     switch (subid)
       {
+#ifdef SUPPORT_SOCKS
+      case '2':        /* proxy information; must arrive before A0 and applies to that addr XXX oops*/
+       proxy_session = TRUE;   /*XXX shouod this be cleared somewhere? */
+       if (*ptr == 0)
+         ptr++;
+       else
+         {
+         proxy_local_address = string_copy(ptr);
+         while(*ptr++);
+         memcpy(&proxy_local_port, ptr, sizeof(proxy_local_port));
+         ptr += sizeof(proxy_local_port);
+         }
+       break;
+#endif
+
 #ifdef EXPERIMENTAL_DSN_INFO
       case '1':        /* must arrive before A0, and applies to that addr */
                /* Two strings: smtp_greeting and helo_response */
@@ -4441,15 +4469,13 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 #ifdef SUPPORT_TLS
       if (addr->cipher)
         {
-        ptr = big_buffer;
-        sprintf(CS ptr, "%.128s", addr->cipher);
-        while(*ptr++);
+        ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->cipher) + 1;
         if (!addr->peerdn)
          *ptr++ = 0;
        else
           {
-          sprintf(CS ptr, "%.512s", addr->peerdn);
-          while(*ptr++);
+          ptr += sprintf(CS ptr, "%.512s", addr->peerdn);
+          ptr++;
           }
 
         rmt_dlv_checked_write(fd, 'X', '1', big_buffer, ptr - big_buffer);
@@ -4475,9 +4501,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 # ifndef DISABLE_OCSP
       if (addr->ocsp > OCSP_NOT_REQ)
        {
-       ptr = big_buffer;
-       sprintf(CS ptr, "%c", addr->ocsp + '0');
-       while(*ptr++);
+       ptr = big_buffer + sprintf(CS big_buffer, "%c", addr->ocsp + '0') + 1;
         rmt_dlv_checked_write(fd, 'X', '4', big_buffer, ptr - big_buffer);
        }
 # endif
@@ -4485,23 +4509,17 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 
       if (client_authenticator)
         {
-        ptr = big_buffer;
-       sprintf(CS big_buffer, "%.64s", client_authenticator);
-        while(*ptr++);
+       ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticator) + 1;
         rmt_dlv_checked_write(fd, 'C', '1', big_buffer, ptr - big_buffer);
        }
       if (client_authenticated_id)
         {
-        ptr = big_buffer;
-       sprintf(CS big_buffer, "%.64s", client_authenticated_id);
-        while(*ptr++);
+        ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticated_id) + 1;
         rmt_dlv_checked_write(fd, 'C', '2', big_buffer, ptr - big_buffer);
        }
       if (client_authenticated_sender)
         {
-        ptr = big_buffer;
-       sprintf(CS big_buffer, "%.64s", client_authenticated_sender);
-        while(*ptr++);
+        ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticated_sender) + 1;
         rmt_dlv_checked_write(fd, 'C', '3', big_buffer, ptr - big_buffer);
        }
 
@@ -4532,19 +4550,34 @@ for (delivery_count = 0; addr_remote; delivery_count++)
         rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer);
         }
 
+#ifdef SUPPORT_SOCKS
+      if (LOGGING(proxy) && proxy_session)
+       {
+       ptr = big_buffer;
+       if (proxy_local_address)
+         {
+         DEBUG(D_deliver) debug_printf("proxy_local_address '%s'\n", proxy_local_address);
+         ptr = big_buffer + sprintf(CS ptr, "%.128s", proxy_local_address) + 1;
+         DEBUG(D_deliver) debug_printf("proxy_local_port %d\n", proxy_local_port);
+         memcpy(ptr, &proxy_local_port, sizeof(proxy_local_port));
+         ptr += sizeof(proxy_local_port);
+         }
+       else
+         *ptr++ = '\0';
+       rmt_dlv_checked_write(fd, 'A', '2', big_buffer, ptr - big_buffer);
+       }
+#endif
+
 #ifdef EXPERIMENTAL_DSN_INFO
 /*um, are they really per-addr?  Other per-conn stuff is not (auth, tls).  But host_used is! */
       if (addr->smtp_greeting)
        {
-       ptr = big_buffer;
        DEBUG(D_deliver) debug_printf("smtp_greeting '%s'\n", addr->smtp_greeting);
-        sprintf(CS ptr, "%.128s", addr->smtp_greeting);
-        while(*ptr++);
+       ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->smtp_greeting) + 1;
        if (addr->helo_response)
          {
          DEBUG(D_deliver) debug_printf("helo_response '%s'\n", addr->helo_response);
-         sprintf(CS ptr, "%.128s", addr->helo_response);
-         while(*ptr++);
+         ptr += sprintf(CS ptr, "%.128s", addr->helo_response) + 1;
          }
        else
          *ptr++ = '\0';
@@ -4554,8 +4587,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 
       /* The rest of the information goes in an 'A0' item. */
 
-      sprintf(CS big_buffer, "%c%c", addr->transport_return,
-        addr->special_action);
+      sprintf(CS big_buffer, "%c%c", addr->transport_return, addr->special_action);
       ptr = big_buffer + 2;
       memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno));
       ptr += sizeof(addr->basic_errno);
@@ -4565,23 +4597,15 @@ for (delivery_count = 0; addr_remote; delivery_count++)
       ptr += sizeof(addr->flags);
 
       if (!addr->message) *ptr++ = 0; else
-        {
-        sprintf(CS ptr, "%.1024s", addr->message);
-        while(*ptr++);
-        }
+        ptr += sprintf(CS ptr, "%.1024s", addr->message) + 1;
 
       if (!addr->user_message) *ptr++ = 0; else
-        {
-        sprintf(CS ptr, "%.1024s", addr->user_message);
-        while(*ptr++);
-        }
+        ptr += sprintf(CS ptr, "%.1024s", addr->user_message) + 1;
 
       if (!addr->host_used) *ptr++ = 0; else
         {
-        sprintf(CS ptr, "%.256s", addr->host_used->name);
-        while(*ptr++);
-        sprintf(CS ptr, "%.64s", addr->host_used->address);
-        while(*ptr++);
+        ptr += sprintf(CS ptr, "%.256s", addr->host_used->name) + 1;
+        ptr += sprintf(CS ptr, "%.64s", addr->host_used->address) + 1;
         memcpy(ptr, &(addr->host_used->port), sizeof(addr->host_used->port));
         ptr += sizeof(addr->host_used->port);
 
@@ -4600,12 +4624,9 @@ for (delivery_count = 0; addr_remote; delivery_count++)
     if (LOGGING(incoming_interface) && sending_ip_address)
 #endif
       {
-      uschar * ptr = big_buffer;
-      sprintf(CS ptr, "%.128s", sending_ip_address);
-      while(*ptr++);
-      sprintf(CS ptr, "%d", sending_port);
-      while(*ptr++);
-
+      uschar * ptr;
+      ptr = big_buffer + sprintf(CS big_buffer, "%.128s", sending_ip_address) + 1;
+      ptr += sprintf(CS ptr, "%d", sending_port) + 1;
       rmt_dlv_checked_write(fd, 'I', '0', big_buffer, ptr - big_buffer);
       }
 
index f3baee9af3a3f5a6dd9329763451a4618fb6f900..a5f14364cffffda1d661c5872a5c315bb9ec8aca 100644 (file)
@@ -615,12 +615,12 @@ static var_entry var_table[] = {
   { "prdr_requested",      vtype_bool,        &prdr_requested },
 #endif
   { "primary_hostname",    vtype_stringptr,   &primary_hostname },
-#ifdef SUPPORT_PROXY
-  { "proxy_host_address",  vtype_stringptr,   &proxy_host_address },
-  { "proxy_host_port",     vtype_int,         &proxy_host_port },
+#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS)
+  { "proxy_external_address",vtype_stringptr, &proxy_external_address },
+  { "proxy_external_port", vtype_int,         &proxy_external_port },
+  { "proxy_local_address", vtype_stringptr,   &proxy_local_address },
+  { "proxy_local_port",    vtype_int,         &proxy_local_port },
   { "proxy_session",       vtype_bool,        &proxy_session },
-  { "proxy_target_address",vtype_stringptr,   &proxy_target_address },
-  { "proxy_target_port",   vtype_int,         &proxy_target_port },
 #endif
   { "prvscheck_address",   vtype_stringptr,   &prvscheck_address },
   { "prvscheck_keynum",    vtype_stringptr,   &prvscheck_keynum },
index fbfb9b8a2afc14fd3c75ddf2e242d4eed31e0272..6bd33a1a574959f9f690571574a5cec67cf2af65 100644 (file)
@@ -875,7 +875,7 @@ bit_table log_options[]        = { /* must be in alphabetical order */
   BIT_TABLE(L, outgoing_interface),
   BIT_TABLE(L, outgoing_port),
   BIT_TABLE(L, pid),
-#ifdef SUPPORT_PROXY
+#if defined(SUPPORT_PROXY) || defined (SUPPORT_SOCKS)
   BIT_TABLE(L, proxy),
 #endif
   BIT_TABLE(L, queue_run),
@@ -1001,14 +1001,14 @@ int     process_info_len       = 0;
 uschar *process_log_path       = NULL;
 BOOL    prod_requires_admin    = TRUE;
 
-#ifdef SUPPORT_PROXY
+#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS)
 uschar *hosts_proxy            = US"";
-uschar *proxy_host_address     = US"";
-int     proxy_host_port        = 0;
+uschar *proxy_external_address = US"";
+int     proxy_external_port    = 0;
+uschar *proxy_local_address    = US"";
+int     proxy_local_port       = 0;
 BOOL    proxy_session          = FALSE;
 BOOL    proxy_session_failed   = FALSE;
-uschar *proxy_target_address   = US"";
-int     proxy_target_port      = 0;
 #endif
 
 uschar *prvscheck_address      = NULL;
index 4263e104dbd15680f3bf3dae2cb9f521f1609f43..899471116f81aa647e32d6a59e98b72765fdcf89 100644 (file)
@@ -645,14 +645,14 @@ extern int     process_info_len;
 extern uschar *process_log_path;       /* Alternate path */
 extern BOOL    prod_requires_admin;    /* TRUE if prodding requires admin */
 
-#ifdef SUPPORT_PROXY
+#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS)
 extern uschar *hosts_proxy;            /* Hostlist which (require) use proxy protocol */
-extern uschar *proxy_host_address;     /* IP of host being proxied */
-extern int     proxy_host_port;        /* Port of host being proxied */
+extern uschar *proxy_external_address; /* IP of remote interface of proxy */
+extern int     proxy_external_port;    /* Port on remote interface of proxy */
+extern uschar *proxy_local_address;    /* IP of local interface of proxy */
+extern int     proxy_local_port;       /* Port on local interface of proxy */
 extern BOOL    proxy_session;          /* TRUE if receiving mail from valid proxy  */
 extern BOOL    proxy_session_failed;   /* TRUE if required proxy negotiation failed */
-extern uschar *proxy_target_address;   /* IP of proxy server inbound */
-extern int     proxy_target_port;      /* Port of proxy server inbound */
 #endif
 
 extern uschar *prvscheck_address;      /* Set during prvscheck expansion item */
index 01f461650541e5d8aea050786087fa033fe80905..dc228d92175b32244ba3b20826df5153018e7df3 100644 (file)
@@ -3777,7 +3777,7 @@ if (prdr_requested)
 
 #ifdef SUPPORT_PROXY
 if (proxy_session && LOGGING(proxy))
-  s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_host_address);
+  s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_local_address);
 #endif
 
 sprintf(CS big_buffer, "%d", msg_size);
index d99f02e6989458a42c820eec9eaf5f96b120ea02..a5ed2f9b7725066b9a6d1a60bf8de52b6cbb810a 100644 (file)
@@ -761,10 +761,10 @@ if (ret >= 16 &&
             DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
             return ERRNO_PROXYFAIL;
             }
-          proxy_host_address  = sender_host_address;
+          proxy_local_address = sender_host_address;
           sender_host_address = string_copy(US tmpip);
           tmpport             = ntohs(hdr.v2.addr.ip4.src_port);
-          proxy_host_port     = sender_host_port;
+          proxy_local_port    = sender_host_port;
           sender_host_port    = tmpport;
           /* Save dest ip/port */
           tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.dst_addr;
@@ -787,10 +787,10 @@ if (ret >= 16 &&
             DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
             return ERRNO_PROXYFAIL;
             }
-          proxy_host_address  = sender_host_address;
+          proxy_local_address = sender_host_address;
           sender_host_address = string_copy(US tmpip6);
           tmpport             = ntohs(hdr.v2.addr.ip6.src_port);
-          proxy_host_port     = sender_host_port;
+          proxy_local_port    = sender_host_port;
           sender_host_port    = tmpport;
           /* Save dest ip/port */
           memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.dst_addr, 16);
@@ -881,7 +881,7 @@ else if (ret >= 8 &&
       debug_printf("Proxied src arg is not an %s address\n", iptype);
     goto proxyfail;
     }
-  proxy_host_address = sender_host_address;
+  proxy_local_address = sender_host_address;
   sender_host_address = p;
   p = sp + 1;
   if ((sp = Ustrchr(p, ' ')) == NULL)
@@ -912,7 +912,7 @@ else if (ret >= 8 &&
       debug_printf("Proxied src port '%s' not an integer\n", p);
     goto proxyfail;
     }
-  proxy_host_port = sender_host_port;
+  proxy_local_port = sender_host_port;
   sender_host_port = tmp_port;
   p = sp + 1;
   if ((sp = Ustrchr(p, '\0')) == NULL)
index 30eded54568dafcf44f681fda403ba214fef7aa4..33b25d1da3bde08d24f70b8381de81e3278fbdac 100644 (file)
@@ -290,7 +290,11 @@ for(;;)
 
   if ((fd = smtp_sock_connect(&proxy, proxy_af, sob->port,
              interface, tb, sob->timeout)) >= 0)
+    {
+    proxy_local_address = string_copy(proxy.address);
+    proxy_local_port = sob->port;
     break;
+    }
 
   log_write(0, LOG_MAIN, "%s: %s", __FUNCTION__, strerror(errno));
   sob->is_failed = TRUE;
@@ -373,11 +377,13 @@ if (  buf[0] != 5
    )
   goto proxy_err;
 
-/*XXX log proxy outbound addr/port? */
+proxy_external_address = string_copy(
+  host_ntoa(buf[3] == 4 ? AF_INET6 : AF_INET, buf+4, NULL, NULL));
+proxy_external_port = ntohs(*((uint16_t *)(buf + (buf[3] == 4 ? 20 : 8))));
+proxy_session = TRUE;
+
 HDEBUG(D_transport|D_acl|D_v)
-  debug_printf("  proxy farside local: [%s]:%d\n",
-    host_ntoa(buf[3] == 4 ? AF_INET6 : AF_INET, buf+4, NULL, NULL),
-    ntohs(*((uint16_t *)(buf + (buf[3] == 4 ? 20 : 8)))));
+  debug_printf("  proxy farside: [%s]:%d\n", proxy_external_address, proxy_external_port);
 
 return fd;
 
index 8e2f6b0527caa2f729a4fcb2d0151f05e11ebdcd..794272e6d119b54be8b39ace566721296259ee52 100644 (file)
@@ -13,6 +13,8 @@ tls_advertise_hosts =
 
 # ----- Main settings -----
 
+log_selector = +proxy +outgoing_port
+
 domainlist local_domains = test.ex : *.test.ex
 acl_smtp_rcpt = accept
 
@@ -38,7 +40,7 @@ my_smtp:
   driver = smtp
   interface = HOSTIPV4
   port = PORT_S
-  socks_proxy = 127.0.0.1 port=PORT_S OPT
+  socks_proxy = 127.0.0.1 port=PORT_D OPT
   debug_print = transport_name <$transport_name>
 
 
index f289beffd74f322a3eed62dcd9a0001f00b585c2..b5e1f7bef7bf0bd3e8216077a63213b9bc788cf9 100644 (file)
@@ -1,6 +1,6 @@
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
-1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1] C="250 accepted OK"
+1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:1224 PRX=[127.0.0.1]:1225 C="250 accepted OK"
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
-1999-03-02 09:44:33 10HmaY-0005vi-00 => userx@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1] C="250 accepted OK"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => userx@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:1224 PRX=[127.0.0.1]:1225 C="250 accepted OK"
 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
index 49d97c05f7dad0fde26a0332222a57e72884eeaf..44c885b8d84cf51473666f45b3b6d4256d8c0497 100644 (file)
@@ -3,7 +3,7 @@
 munge loopback
 #
 # auth: null
-server PORT_S
+server PORT_D
 <<\x05\x01\x00
 >>\x05\x00
 <<\x05\x01\x00\x01\x7f\x00\x00\x01\x04\xc8
@@ -42,7 +42,7 @@ quit
 #
 #
 # auth: username/password
-server PORT_S
+server PORT_D
 <<\x05\x01\x02
 >>\x05\x02
 <<\x01\x04fred\x05fubar
index 720c954fdb76eac20c0408040f06706d7fa4f6f6..293f5136e3830dd3499b8169e4dcd68fdbb41c0c 100644 (file)
@@ -22,7 +22,7 @@
 221 myhost.test.ex closing connection\r
 
 ******** SERVER ********
-Listening on port 1224 ... 
+Listening on port 1225 ... 
 Connection request from [ip4.ip4.ip4.ip4]
 <<\x05\x01\x00
 >>\x05\x00
@@ -43,7 +43,7 @@ R
 QUIT
 250 bye
 End of script
-Listening on port 1224 ... 
+Listening on port 1225 ... 
 Connection request from [ip4.ip4.ip4.ip4]
 <<\x05\x01\x02
 >>\x05\x02