From e293ad1b08b2f937555a102e6f3b4336574773c8 Mon Sep 17 00:00:00 2001 From: Serge Aleynikov Date: Wed, 30 Dec 2015 13:29:34 -0500 Subject: Assign externally open fd to gen_tcp (UDS support) When a AF_LOCAL file descriptor is created externally (e.g. Unix Domain Socket) and passed to `gen_tcp:listen(0, [{fd, FD}])`, the implementation incorrectly assigned the address family to be equal to `inet`, which in the inet_drv driver translated to AF_INET instead of AF_LOCAL (or AF_UNIX), and an `einval` error code was returned. This patch fixes this problem such that the file descriptors of the `local` address family are supported in the inet:fdopen/5, gen_tcp:connect/3, gen_tcp:listen/2, gen_udp:open/2 calls --- erts/emulator/drivers/common/inet_drv.c | 161 +++++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 13 deletions(-) (limited to 'erts/emulator/drivers/common') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 89011d89ad..bd4543f831 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -58,6 +58,9 @@ #ifdef HAVE_NETPACKET_PACKET_H #include #endif +#ifdef HAVE_SYS_UN_H +#include +#endif /* All platforms fail on malloc errors. */ #define FATAL_MALLOC @@ -747,6 +750,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_AF_INET6 2 #define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */ #define INET_AF_LOOPBACK 4 /* INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT */ +#define INET_AF_LOCAL 5 /* open and INET_REQ_GETTYPE enumeration */ #define INET_TYPE_STREAM 1 @@ -1032,19 +1036,29 @@ typedef union { #ifdef HAVE_IN6 struct sockaddr_in6 sai6; #endif +#ifdef HAVE_SYS_UN_H + struct sockaddr_un sal; +#endif } inet_address; /* for AF_INET & AF_INET6 */ #define inet_address_port(x) ((x)->sai.sin_port) +#ifdef HAVE_SYS_UN_H +#define localaddrlen(family, data) \ + ((family == AF_LOCAL) ? *(unsigned char*)(data) : 0) +#else + 0 +#endif + #if defined(HAVE_IN6) && defined(AF_INET6) -#define addrlen(family) \ +#define addrlen(family, data) \ ((family == AF_INET) ? sizeof(struct in_addr) : \ - ((family == AF_INET6) ? sizeof(struct in6_addr) : 0)) + ((family == AF_INET6) ? sizeof(struct in6_addr) : localaddrlen(family, data))) #else -#define addrlen(family) \ - ((family == AF_INET) ? sizeof(struct in_addr) : 0) +#define addrlen(family, data) \ + ((family == AF_INET) ? sizeof(struct in_addr) : localaddrlen(family, data)) #endif typedef struct _multi_timer_data { @@ -1727,6 +1741,12 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) spec[i++] = ERL_DRV_TUPLE; spec[i++] = 8; } +#endif +#ifdef HAVE_SYS_UN_H + else if (family == AF_LOCAL) { + int len = *(unsigned char*)buf++; + i = LOAD_STRING(spec, i, buf, len); + } #endif else { spec[i++] = ERL_DRV_TUPLE; @@ -3573,10 +3593,11 @@ static int tcp_error_message(tcp_descriptor* desc, int err) #ifdef HAVE_UDP /* ** active mode message: -** {udp, S, IP, Port, [H1,...Hsz | Data]} or -** {sctp, S, IP, Port, {[AncilData], Event_or_Data}} +** {udp, S, IP, Port, [H1,...Hsz | Data]} or +** {sctp, S, IP, Port, {[AncilData], Event_or_Data}} ** where ** [H1,...,HSz] are msg headers (without IP/Port, UDP only), +** [AddrLen, H2,...,HSz] are msg headers for UDP AF_LOCAL only ** Data : List() | Binary() */ static int packet_binary_message @@ -3586,6 +3607,7 @@ static int packet_binary_message ErlDrvTermData spec [PACKET_ERL_DRV_TERM_DATA_LEN]; int i = 0; int alen; + char* data = bin->orig_bytes+offs; DEBUGF(("packet_binary_message(%ld): len = %d\r\n", (long)desc->port, len)); @@ -3596,10 +3618,15 @@ static int packet_binary_message # endif i = LOAD_PORT(spec, i, desc->dport); /* S */ - alen = addrlen(desc->sfamily); - i = load_ip_address(spec, i, desc->sfamily, bin->orig_bytes+offs+3); - i = load_ip_port(spec, i, bin->orig_bytes+offs+1); /* IP, Port */ + alen = addrlen(desc->sfamily, data+3); + i = load_ip_address(spec, i, desc->sfamily, data+3); + i = load_ip_port(spec, i, data+1); /* IP, Port */ +# ifdef HAVE_SYS_UN_H + /* AF_LOCAL addresses have a prefix byte containing address length */ + if (desc->sfamily == AF_LOCAL) + alen++; +# endif offs += (alen + 3); len -= (alen + 3); @@ -4151,6 +4178,16 @@ static char* inet_set_address(int family, inet_address* dst, *len = sizeof(struct sockaddr_in6); return src + 2+16; } +#endif +#ifdef HAVE_SYS_UN_H + else if ((family == AF_LOCAL) && (*len >= 3+sizeof(struct sockaddr_un))) { + int n = *((unsigned char*)src+2); + dst->sal.sun_family = family; + sys_memcpy(dst->sal.sun_path, src+3, n); + dst->sal.sun_path[n-1] = '\0'; + *len = n; + return src + 3 + n; + } #endif return NULL; } @@ -4160,7 +4197,7 @@ static char* inet_set_address(int family, inet_address* dst, ** or from argument if source data specifies constant address. ** ** src = [TAG,P1,P0] when TAG = INET_AF_ANY | INET_AF_LOOPBACK -** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 +** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL */ static char *inet_set_faddress(int family, inet_address* dst, char *src, ErlDrvSizeT* len) { @@ -4177,6 +4214,21 @@ static char *inet_set_faddress(int family, inet_address* dst, case INET_AF_INET6: family = AF_INET6; break; +# endif +# ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: { + int n; + if (*len || *len < 3) return NULL; + family = AF_LOCAL; + /* Next two bytes are the length of the local path (< 256) */ + src++; + n = *(unsigned char*)src++; + if (n+3 > *len) return NULL; + dst->sal.sun_family = family; + sys_memcpy(dst->sal.sun_path, src, n); + *len = n; + break; + } # endif case INET_AF_ANY: case INET_AF_LOOPBACK: { @@ -4241,7 +4293,6 @@ static char *inet_set_faddress(int family, inet_address* dst, return inet_set_address(family, dst, src, len); } - /* Get a inaddr structure ** src = inaddr structure ** *len is the lenght of structure @@ -4273,10 +4324,55 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) *len = 3 + sizeof(struct in6_addr); return 0; } +#endif +#ifdef HAVE_SYS_UN_H + else if ((family == AF_LOCAL) && *len > 0) { + int n = *len - 4; + dst[0] = INET_AF_LOCAL; + put_int16(0, dst+1); + if (n == 0 || n >= sizeof(src->sal.sun_path)) { + *(dst+3) = 0; + *len = 3+1; + } else { + *(dst+3) = n; + sys_memcpy(dst+4, src->sal.sun_path, n); + *len = 3+1+n; + } + return 0; + } #endif return -1; } +static int inet_family_get_address(inet_descriptor* desc, char* dst, inet_address* src, unsigned int* len) +{ +#ifdef HAVE_SYS_UN_H + if (desc->sfamily == AF_LOCAL) { + int n = *len - 4; + dst[0] = INET_AF_LOCAL; + put_int16(0, dst+1); + if (n <= 0 || n >= sizeof(src->sal.sun_path)) { + if (desc->name_ptr) { + char* p = desc->name_ptr->sal.sun_path; + n = strlen(p); + *(dst+3) = n; + sys_memcpy(dst+4, p, n); + *len = 3+1+n; + } else { + *(dst+3) = 0; + *len = 3+1; + } + } else { + *(dst+3) = n; + sys_memcpy(dst+4, src->sal.sun_path, n); + *len = 3+1+n; + } + return 0; + } +#endif + return inet_get_address(dst, src, len); +} + /* Same as the above, but take family from the address structure, ** and advance the address pointer to the next address ** according to the size of the current, @@ -4306,6 +4402,19 @@ static int inet_address_to_erlang(char *dst, inet_address **src) { } (*src) = (inet_address *) (&(*src)->sai6 + 1); return 1 + 2 + 16; +#endif +#ifdef HAVE_SYS_UN_H + case AF_LOCAL: { + int n = strlen((*src)->sal.sun_path); + if (dst) { + dst[0] = INET_AF_LOCAL; + put_int16(0, dst+1); + *(dst+3) = n; + sys_memcpy(dst+1+2+1, (*src)->sal.sun_path, n); + } + (*src) = (inet_address *) (&(*src)->sal + 1); + return 1+2+1+n; + } #endif default: return -1; @@ -4402,7 +4511,8 @@ static void desc_close_read(inet_descriptor* desc) static int erl_inet_close(inet_descriptor* desc) { free_subscribers(&desc->empty_out_q_subs); - if ((desc->prebound == 0) && (desc->state & INET_F_OPEN)) { + if ((desc->prebound == 0 || desc->sfamily == AF_LOCAL) && + (desc->state & INET_F_OPEN)) { desc_close(desc); desc->state = INET_STATE_CLOSED; } else if (desc->prebound && (desc->s != INVALID_SOCKET)) { @@ -4582,6 +4692,13 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, return ctl_error(sock_errno(), rbuf, rsize); if (name.sa.sa_family != domain) return ctl_error(EINVAL, rbuf, rsize); +#ifdef HAVE_SYS_UN_H + if (domain == AF_LOCAL) { + sys_memcpy(&desc->name_addr, &name, sizeof(desc->name_addr)); + if (desc->name_ptr == NULL) + desc->name_ptr = &desc->name_addr; + } +#endif } #ifdef __OSE__ /* for fdopen duplicating the sd will allow to uniquely identify @@ -8570,6 +8687,11 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, else if (desc->sfamily == AF_INET6) { put_int32(INET_AF_INET6, &tbuf[0]); } +#endif +#ifdef HAVE_SYS_UN_H + else if (desc->sfamily == AF_LOCAL) { + put_int32(INET_AF_LOCAL, &tbuf[0]); + } #endif else return ctl_error(EINVAL, rbuf, rsize); @@ -9255,6 +9377,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_AF_INET6: return ctl_xerror("eafnosupport", rbuf, rsize); break; +#endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: + domain = AF_LOCAL; + break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -9281,6 +9408,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, case INET_AF_INET6: return ctl_xerror("eafnosupport", rbuf, rsize); break; +#endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: + domain = AF_LOCAL; + break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -11343,6 +11475,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, case INET_AF_INET6: return ctl_xerror("eafnosupport", rbuf, rsize); break; +#endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: af = AF_LOCAL; break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -11955,7 +12090,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) inet_input_count(desc, n); udesc->i_ptr += n; - inet_get_address(abuf, &other, &len); + inet_family_get_address(desc, abuf, &other, &len); /* Copy formatted address to the buffer allocated; "len" is the actual length which must be <= than the original reserved. This means that the addr + data in the buffer are contiguous, -- cgit v1.2.3 From 011954e851d421b882b0b4eaeda04cfc2895d70a Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 4 May 2016 14:09:04 +0200 Subject: Rewrite inet_drv for AF_LOCAL --- erts/emulator/drivers/common/inet_drv.c | 384 ++++++++++++++++++-------------- 1 file changed, 215 insertions(+), 169 deletions(-) (limited to 'erts/emulator/drivers/common') diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index bd4543f831..71e321df26 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2015. All Rights Reserved. + * Copyright Ericsson AB 1997-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -712,7 +713,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #ifdef HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t #else -# define SOCKLEN_T int +# define SOCKLEN_T size_t #endif #include "packet_parser.h" @@ -726,6 +727,22 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) (((unsigned char*) (s))[1] << 8) | \ (((unsigned char*) (s))[0])) +/* strnlen doesn't exist everywhere */ +static size_t my_strnlen(const char *s, size_t maxlen) +{ + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; +} + +/* Check that some character in the buffer != '\0' */ +static int is_nonzero(const char *s, size_t n) +{ + size_t i; + for (i = 0; i < n; i++) if (s[i] != '\0') return !0; + return 0; +} #ifdef VALGRIND # include @@ -751,6 +768,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */ #define INET_AF_LOOPBACK 4 /* INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT */ #define INET_AF_LOCAL 5 +#define INET_AF_UNDEFINED 6 /* Unknown */ /* open and INET_REQ_GETTYPE enumeration */ #define INET_TYPE_STREAM 1 @@ -1042,23 +1060,29 @@ typedef union { } inet_address; -/* for AF_INET & AF_INET6 */ -#define inet_address_port(x) ((x)->sai.sin_port) +#define inet_address_port(x) \ + ((((x)->sai.sin_family == AF_INET) || \ + ((x)->sai.sin_family == AF_INET6)) ? \ + ((x)->sai.sin_port) : -1) #ifdef HAVE_SYS_UN_H -#define localaddrlen(family, data) \ - ((family == AF_LOCAL) ? *(unsigned char*)(data) : 0) +#define localaddrlen(data) \ + ((((unsigned char*)(data))[0] == INET_AF_LOCAL) ? \ + (1 + 1 + ((unsigned char*)(data))[1]) : 1) #else - 0 +#define localaddrlen(data) (1) #endif #if defined(HAVE_IN6) && defined(AF_INET6) -#define addrlen(family, data) \ - ((family == AF_INET) ? sizeof(struct in_addr) : \ - ((family == AF_INET6) ? sizeof(struct in6_addr) : localaddrlen(family, data))) +#define addrlen(data) \ + ((((unsigned char*)(data))[0] == INET_AF_INET) ? \ + (1 + 2 + 4) : \ + ((((unsigned char*)(data))[0] == INET_AF_INET6) ? \ + (1 + 2 + 16) : localaddrlen(data))) #else -#define addrlen(family, data) \ - ((family == AF_INET) ? sizeof(struct in_addr) : localaddrlen(family, data)) +#define addrlen(data) \ + ((((unsigned char*)(data))[0] == INET_AF_INET) ? \ + (1 + 2 + 4) : localaddrlen(data)) #endif typedef struct _multi_timer_data { @@ -1149,8 +1173,10 @@ typedef struct { inet_address peer_addr; /* fake peer address */ inet_address name_addr; /* fake local address */ - inet_address* peer_ptr; /* fake peername or NULL */ - inet_address* name_ptr; /* fake sockname or NULL */ + inet_address* peer_ptr; /* fake peername or NULL */ + inet_address* name_ptr; /* fake sockname or NULL */ + SOCKLEN_T peer_addr_len; /* fake peername size */ + SOCKLEN_T name_addr_len; /* fake sockname size */ int bufsz; /* minimum buffer constraint */ unsigned int hsz; /* the list header size, -1 is large !!! */ @@ -1489,6 +1515,7 @@ static int async_ref = 0; /* async reference id generator */ } while (0) static ErlDrvTermData am_ok; +static ErlDrvTermData am_undefined; static ErlDrvTermData am_tcp; static ErlDrvTermData am_error; static ErlDrvTermData am_einval; @@ -1505,6 +1532,7 @@ static ErlDrvTermData am_ssl_tls; static ErlDrvTermData am_udp; static ErlDrvTermData am_udp_passive; static ErlDrvTermData am_udp_error; +static ErlDrvTermData am_local; #endif #ifdef HAVE_SCTP static ErlDrvTermData am_sctp; @@ -1714,46 +1742,59 @@ static void *realloc_wrapper(void *current, ErlDrvSizeT size){ #endif #ifdef HAVE_UDP -static int load_ip_port(ErlDrvTermData* spec, int i, char* buf) -{ - spec[i++] = ERL_DRV_INT; - spec[i++] = (ErlDrvTermData) get_int16(buf); - return i; -} - -static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) +static int load_address(ErlDrvTermData* spec, int i, char* buf) { int n; - if (family == AF_INET) { - for (n = 0; n < 4; n++) { + switch (*buf++) { /* Family */ + case INET_AF_INET: { + for (n = 2; n < 2+4; n++) { spec[i++] = ERL_DRV_INT; spec[i++] = (ErlDrvTermData) ((unsigned char)buf[n]); } spec[i++] = ERL_DRV_TUPLE; spec[i++] = 4; + spec[i++] = ERL_DRV_INT; + spec[i++] = (ErlDrvTermData) get_int16(buf); + break; } #if defined(HAVE_IN6) && defined(AF_INET6) - else if (family == AF_INET6) { - for (n = 0; n < 16; n += 2) { + case INET_AF_INET6: { + for (n = 2; n < 2+16; n += 2) { spec[i++] = ERL_DRV_INT; spec[i++] = (ErlDrvTermData) get_int16(buf+n); } spec[i++] = ERL_DRV_TUPLE; spec[i++] = 8; + spec[i++] = ERL_DRV_INT; + spec[i++] = (ErlDrvTermData) get_int16(buf); + break; } #endif #ifdef HAVE_SYS_UN_H - else if (family == AF_LOCAL) { + case INET_AF_LOCAL: { int len = *(unsigned char*)buf++; - i = LOAD_STRING(spec, i, buf, len); + i = LOAD_ATOM(spec, i, am_local); + i = LOAD_BUF2BINARY(spec, i, buf, len); + spec[i++] = ERL_DRV_TUPLE; + spec[i++] = 2; + spec[i++] = ERL_DRV_INT; + spec[i++] = 0; + break; } #endif - else { + default: { /* INET_AF_UNDEFINED */ + i = LOAD_ATOM(spec, i, am_undefined); + spec[i++] = ERL_DRV_INT; + spec[i++] = 0; spec[i++] = ERL_DRV_TUPLE; + spec[i++] = 2; + spec[i++] = ERL_DRV_INT; spec[i++] = 0; + break; + } } return i; -} + } #endif @@ -1761,10 +1802,13 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) /* For SCTP, we often need to return {IP, Port} tuples: */ static int inet_get_address(char* dst, inet_address* src, unsigned int* len); -#define LOAD_IP_AND_PORT_CNT \ +/* Max of {{int()*8},int()} | {{int()*4},int()} | + * {{'local',binary()},int()} + */ +#define LOAD_INET_GET_ADDRESS_CNT \ (8*LOAD_INT_CNT + LOAD_TUPLE_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT) -static int load_ip_and_port +static int load_inet_get_address (ErlDrvTermData* spec, int i, inet_descriptor* desc, struct sockaddr_storage* addr) { @@ -1782,8 +1826,7 @@ static int load_ip_and_port /* NB: the following functions are safe to use, as they create tuples of copied Ints on the "spec", and do not install any String pts -- a ptr to "abuf" would be dangling upon exiting this function: */ - i = load_ip_address(spec, i, desc->sfamily, abuf+3); - i = load_ip_port (spec, i, abuf+1); + i = load_address(spec, i, abuf); /* IP,Port | Family,Addr */ i = LOAD_TUPLE (spec, i, 2); return i; } @@ -2475,7 +2518,6 @@ static ErlDrvTermData am_http_error; static ErlDrvTermData am_abs_path; static ErlDrvTermData am_absoluteURI; static ErlDrvTermData am_star; -static ErlDrvTermData am_undefined; static ErlDrvTermData am_http; static ErlDrvTermData am_https; static ErlDrvTermData am_scheme; @@ -3166,7 +3208,7 @@ static int sctp_parse_async_event ASSERT(sptr->spc_length <= sz); /* No buffer overrun */ i = LOAD_ATOM (spec, i, am_sctp_paddr_change); - i = load_ip_and_port(spec, i, desc, &sptr->spc_aaddr); + i = load_inet_get_address(spec, i, desc, &sptr->spc_aaddr); switch (sptr->spc_state) { @@ -3617,18 +3659,12 @@ static int packet_binary_message i = LOAD_ATOM(spec, i, am_udp ); /* UDP only */ # endif i = LOAD_PORT(spec, i, desc->dport); /* S */ - - alen = addrlen(desc->sfamily, data+3); - i = load_ip_address(spec, i, desc->sfamily, data+3); - i = load_ip_port(spec, i, data+1); /* IP, Port */ - -# ifdef HAVE_SYS_UN_H - /* AF_LOCAL addresses have a prefix byte containing address length */ - if (desc->sfamily == AF_LOCAL) - alen++; -# endif - offs += (alen + 3); - len -= (alen + 3); + + alen = addrlen(data); + i = load_address(spec, i, data); /* IP,Port | Family,Addr */ + + offs += alen; + len -= alen; # ifdef HAVE_SCTP if (!IS_SCTP(desc)) @@ -4015,6 +4051,7 @@ static int inet_init() # endif INIT_ATOM(ok); + INIT_ATOM(undefined); INIT_ATOM(tcp); #ifdef HAVE_UDP INIT_ATOM(udp); @@ -4031,6 +4068,7 @@ static int inet_init() #ifdef HAVE_UDP INIT_ATOM(udp_passive); INIT_ATOM(udp_error); + INIT_ATOM(local); #endif INIT_ATOM(empty_out_q); INIT_ATOM(ssl_tls); @@ -4043,7 +4081,6 @@ static int inet_init() INIT_ATOM(abs_path); INIT_ATOM(absoluteURI); am_star = driver_mk_atom("*"); - INIT_ATOM(undefined); INIT_ATOM(http); INIT_ATOM(https); INIT_ATOM(scheme); @@ -4139,7 +4176,7 @@ static int inet_init() /* -** Set a inaddr structure: +** Set an inaddr structure: ** src = [P1,P0,X1,X2,.....] ** dst points to a structure large enugh to keep any kind ** of inaddr. @@ -4180,13 +4217,15 @@ static char* inet_set_address(int family, inet_address* dst, } #endif #ifdef HAVE_SYS_UN_H - else if ((family == AF_LOCAL) && (*len >= 3+sizeof(struct sockaddr_un))) { - int n = *((unsigned char*)src+2); - dst->sal.sun_family = family; - sys_memcpy(dst->sal.sun_path, src+3, n); - dst->sal.sun_path[n-1] = '\0'; - *len = n; - return src + 3 + n; + else if ((family == AF_LOCAL) && (*len >= 1)) { + int n = *((unsigned char*)src); + if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) + return NULL; + sys_memzero((char*)dst, sizeof(struct sockaddr_un)); + dst->sal.sun_family = family; + sys_memcpy(dst->sal.sun_path, src+1, n); + *len = offsetof(struct sockaddr_un, sun_path) + n; + return src + 1 + n; } #endif return NULL; @@ -4217,17 +4256,8 @@ static char *inet_set_faddress(int family, inet_address* dst, # endif # ifdef HAVE_SYS_UN_H case INET_AF_LOCAL: { - int n; - if (*len || *len < 3) return NULL; - family = AF_LOCAL; - /* Next two bytes are the length of the local path (< 256) */ - src++; - n = *(unsigned char*)src++; - if (n+3 > *len) return NULL; - dst->sal.sun_family = family; - sys_memcpy(dst->sal.sun_path, src, n); - *len = n; - break; + family = AF_LOCAL; + break; } # endif case INET_AF_ANY: @@ -4303,6 +4333,7 @@ static char *inet_set_faddress(int family, inet_address* dst, */ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) { + /* Compare the code with inet_address_to_erlang() */ int family; short port; @@ -4326,51 +4357,32 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) } #endif #ifdef HAVE_SYS_UN_H - else if ((family == AF_LOCAL) && *len > 0) { - int n = *len - 4; - dst[0] = INET_AF_LOCAL; - put_int16(0, dst+1); - if (n == 0 || n >= sizeof(src->sal.sun_path)) { - *(dst+3) = 0; - *len = 3+1; - } else { - *(dst+3) = n; - sys_memcpy(dst+4, src->sal.sun_path, n); - *len = 3+1+n; - } - return 0; - } + else if (family == AF_LOCAL) { + size_t n, m; + if (*len < offsetof(struct sockaddr_un, sun_path)) return -1; + n = *len - offsetof(struct sockaddr_un, sun_path); + if (255 < n) return -1; + /* Portability fix: Assume that the address is a zero terminated + * string, except when the first byte is \0 i.e the + * string length is 0. Then use the reported length instead. + * This fix handles Linux's abstract socket address + * nonportable extension. + */ + m = my_strnlen(src->sal.sun_path, n); + if ((m == 0) && is_nonzero(src->sal.sun_path, n)) + m = n; + dst[0] = INET_AF_LOCAL; + dst[1] = (char) ((unsigned char) m); + sys_memcpy(dst+2, src->sal.sun_path, m); + *len = 1 + 1 + m; + return 0; + } #endif - return -1; -} - -static int inet_family_get_address(inet_descriptor* desc, char* dst, inet_address* src, unsigned int* len) -{ -#ifdef HAVE_SYS_UN_H - if (desc->sfamily == AF_LOCAL) { - int n = *len - 4; - dst[0] = INET_AF_LOCAL; - put_int16(0, dst+1); - if (n <= 0 || n >= sizeof(src->sal.sun_path)) { - if (desc->name_ptr) { - char* p = desc->name_ptr->sal.sun_path; - n = strlen(p); - *(dst+3) = n; - sys_memcpy(dst+4, p, n); - *len = 3+1+n; - } else { - *(dst+3) = 0; - *len = 3+1; - } - } else { - *(dst+3) = n; - sys_memcpy(dst+4, src->sal.sun_path, n); - *len = 3+1+n; - } - return 0; + else { + dst[0] = INET_AF_UNDEFINED; + *len = 1; } -#endif - return inet_get_address(dst, src, len); + return -1; } /* Same as the above, but take family from the address structure, @@ -4378,7 +4390,9 @@ static int inet_family_get_address(inet_descriptor* desc, char* dst, inet_addres ** according to the size of the current, ** and return the resulting encoded size */ -static int inet_address_to_erlang(char *dst, inet_address **src) { +static int +inet_address_to_erlang(char *dst, inet_address **src, SOCKLEN_T sz) { + /* Compare the code with inet_get_address() */ short port; switch ((*src)->sa.sa_family) { @@ -4405,15 +4419,26 @@ static int inet_address_to_erlang(char *dst, inet_address **src) { #endif #ifdef HAVE_SYS_UN_H case AF_LOCAL: { - int n = strlen((*src)->sal.sun_path); + size_t n, m; + if (sz < offsetof(struct sockaddr_un, sun_path)) return -1; + n = sz - offsetof(struct sockaddr_un, sun_path); + if (255 < n) return -1; + /* Portability fix: Assume that the address is a zero terminated + * string, except when the first byte is \0 i.e the + * string length is 0. Then use the reported length instead. + * This fix handles Linux's abstract socket address + * nonportable extension. + */ + m = my_strnlen((*src)->sal.sun_path, n); + if ((m == 0) && is_nonzero((*src)->sal.sun_path, n)) + m = n; if (dst) { dst[0] = INET_AF_LOCAL; - put_int16(0, dst+1); - *(dst+3) = n; - sys_memcpy(dst+1+2+1, (*src)->sal.sun_path, n); + dst[1] = (char) ((unsigned char) m); + sys_memcpy(dst+2, (*src)->sal.sun_path, m); } (*src) = (inet_address *) (&(*src)->sal + 1); - return 1+2+1+n; + return 1 + 1 + m; } #endif default: @@ -4424,7 +4449,7 @@ static int inet_address_to_erlang(char *dst, inet_address **src) { /* Encode n encoded addresses from addrs in the result buffer */ static ErlDrvSizeT reply_inet_addrs -(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize) { +(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize, SOCKLEN_T sz) { inet_address *ia; int i, s; ErlDrvSizeT rlen; @@ -4432,11 +4457,19 @@ static ErlDrvSizeT reply_inet_addrs if (IS_SOCKET_ERROR(n)) return ctl_error(sock_errno(), rbuf, rsize); if (n == 0) return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); + /* The sz argument is only used when we have got an actual size + * of addrs[0] from e.g getsockname() and then n == 1 + * so we will loop over 1 element below. Otherwise sz + * would be expected to differ between addresses but that + * can only happen for AF_LOCAL and we will only be called with + * n > 1 for SCTP and that will never (?) happen with AF_LOCAL + */ + /* Calculate result length */ rlen = 1; ia = addrs; for (i = 0; i < n; i++) { - s = inet_address_to_erlang(NULL, &ia); + s = inet_address_to_erlang(NULL, &ia, sz); if (s < 0) break; rlen += s; } @@ -4447,7 +4480,7 @@ static ErlDrvSizeT reply_inet_addrs rlen = 1; ia = addrs; for (i = 0; i < n; i++) { - s = inet_address_to_erlang((*rbuf)+rlen, &ia); + s = inet_address_to_erlang((*rbuf)+rlen, &ia, sz); if (s < 0) break; rlen += s; } @@ -4511,8 +4544,7 @@ static void desc_close_read(inet_descriptor* desc) static int erl_inet_close(inet_descriptor* desc) { free_subscribers(&desc->empty_out_q_subs); - if ((desc->prebound == 0 || desc->sfamily == AF_LOCAL) && - (desc->state & INET_F_OPEN)) { + if ((desc->prebound == 0) && (desc->state & INET_F_OPEN)) { desc_close(desc); desc->state = INET_STATE_CLOSED; } else if (desc->prebound && (desc->s != INVALID_SOCKET)) { @@ -4589,6 +4621,7 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { int save_errno; + int protocol; #ifdef HAVE_SETNS int current_ns, new_ns; current_ns = new_ns = 0; @@ -4627,7 +4660,11 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, } } #endif - if ((desc->s = sock_open(domain, type, desc->sprotocol)) == INVALID_SOCKET) + protocol = desc->sprotocol; +#ifdef HAVE_SYS_UN_H + if (domain == AF_LOCAL) protocol = 0; +#endif + if ((desc->s = sock_open(domain, type, protocol)) == INVALID_SOCKET) save_errno = sock_errno(); #ifdef HAVE_SETNS if (desc->netns != NULL) { @@ -4692,13 +4729,6 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, return ctl_error(sock_errno(), rbuf, rsize); if (name.sa.sa_family != domain) return ctl_error(EINVAL, rbuf, rsize); -#ifdef HAVE_SYS_UN_H - if (domain == AF_LOCAL) { - sys_memcpy(&desc->name_addr, &name, sizeof(desc->name_addr)); - if (desc->name_ptr == NULL) - desc->name_ptr = &desc->name_addr; - } -#endif } #ifdef __OSE__ /* for fdopen duplicating the sd will allow to uniquely identify @@ -7550,14 +7580,14 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, #ifdef HAVE_SCTP #define LOAD_PADDRINFO_CNT \ - (2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + LOAD_IP_AND_PORT_CNT + \ + (2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + LOAD_INET_GET_ADDRESS_CNT + \ 4*LOAD_INT_CNT + LOAD_TUPLE_CNT) static int load_paddrinfo (ErlDrvTermData * spec, int i, inet_descriptor* desc, struct sctp_paddrinfo* pai) { i = LOAD_ATOM (spec, i, am_sctp_paddrinfo); i = LOAD_ASSOC_ID (spec, i, pai->spinfo_assoc_id); - i = load_ip_and_port(spec, i, desc, &pai->spinfo_address); + i = load_inet_get_address(spec, i, desc, &pai->spinfo_address); switch(pai->spinfo_state) { case SCTP_ACTIVE: @@ -7973,7 +8003,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, /* Fill in the response: */ PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + - LOAD_IP_AND_PORT_CNT + 2*LOAD_TUPLE_CNT); + LOAD_INET_GET_ADDRESS_CNT + 2*LOAD_TUPLE_CNT); switch (eopt) { case SCTP_OPT_PRIMARY_ADDR: i = LOAD_ATOM(spec, i, am_sctp_primary_addr); @@ -7987,7 +8017,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, ASSERT(0); } i = LOAD_ASSOC_ID (spec, i, sp.sspp_assoc_id); - i = load_ip_and_port(spec, i, desc, &sp.sspp_addr); + i = load_inet_get_address(spec, i, desc, &sp.sspp_addr); i = LOAD_TUPLE (spec, i, 3); i = LOAD_TUPLE (spec, i, 2); break; @@ -8034,11 +8064,11 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, /* Fill in the response: */ PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + - LOAD_IP_AND_PORT_CNT + 4*LOAD_INT_CNT); + LOAD_INET_GET_ADDRESS_CNT + 4*LOAD_INT_CNT); i = LOAD_ATOM (spec, i, am_sctp_peer_addr_params); i = LOAD_ATOM (spec, i, am_sctp_paddrparams); i = LOAD_ASSOC_ID (spec, i, ap.spp_assoc_id); - i = load_ip_and_port(spec, i, desc, &ap.spp_address); + i = load_inet_get_address(spec, i, desc, &ap.spp_address); i = LOAD_INT (spec, i, ap.spp_hbinterval); i = LOAD_INT (spec, i, ap.spp_pathmaxrxt); @@ -8752,19 +8782,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, assoc_id = get_int32(buf); n = p_sctp_getpaddrs(desc->s, assoc_id, &sa); - rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize, 0); if (n > 0) p_sctp_freepaddrs(sa); return rlen; } #endif { /* Fallback to sock_peer */ inet_address addr; - unsigned int sz; + SOCKLEN_T sz; int i; sz = sizeof(addr); i = sock_peer(desc->s, (struct sockaddr *) &addr, &sz); - return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz); } } @@ -8772,15 +8802,21 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, char tbuf[sizeof(inet_address)]; inet_address peer; inet_address* ptr; - unsigned int sz = sizeof(peer); + unsigned int sz; DEBUGF(("inet_ctl(%ld): PEER\r\n", (long)desc->port)); if (!(desc->state & INET_F_ACTIVE)) return ctl_error(ENOTCONN, rbuf, rsize); - if ((ptr = desc->peer_ptr) == NULL) { + if ((ptr = desc->peer_ptr) != NULL) { + sz = desc->peer_addr_len; + } + else { ptr = &peer; - if (IS_SOCKET_ERROR(sock_peer(desc->s, (struct sockaddr*)ptr,&sz))) + sz = sizeof(peer); + if (IS_SOCKET_ERROR + (sock_peer + (desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); } if (inet_get_address(tbuf, ptr, &sz) < 0) @@ -8795,11 +8831,12 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_address(desc->sfamily, &desc->peer_addr, - buf, &len) == NULL) + else if (inet_set_faddress + (desc->sfamily, &desc->peer_addr, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); else { desc->peer_ptr = &desc->peer_addr; + desc->peer_addr_len = (SOCKLEN_T) len; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } @@ -8821,19 +8858,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, assoc_id = get_int32(buf); n = p_sctp_getladdrs(desc->s, assoc_id, &sa); - rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize, 0); if (n > 0) p_sctp_freeladdrs(sa); return rlen; } #endif { /* Fallback to sock_name */ inet_address addr; - unsigned int sz; + SOCKLEN_T sz; int i; sz = sizeof(addr); i = sock_name(desc->s, (struct sockaddr *) &addr, &sz); - return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz); } } @@ -8841,16 +8878,21 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, char tbuf[sizeof(inet_address)]; inet_address name; inet_address* ptr; - unsigned int sz = sizeof(name); + unsigned int sz; DEBUGF(("inet_ctl(%ld): NAME\r\n", (long)desc->port)); if (!IS_BOUND(desc)) return ctl_error(EINVAL, rbuf, rsize); /* address is not valid */ - if ((ptr = desc->name_ptr) == NULL) { + if ((ptr = desc->name_ptr) != NULL) { + sz = desc->name_addr_len; + } + else { ptr = &name; - if (IS_SOCKET_ERROR(sock_name(desc->s, (struct sockaddr*)ptr, &sz))) + sz = sizeof(name); + if (IS_SOCKET_ERROR + (sock_name(desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); } if (inet_get_address(tbuf, ptr, &sz) < 0) @@ -8858,18 +8900,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize); } - case INET_REQ_SETNAME: { /* set fake peername Port Address */ + case INET_REQ_SETNAME: { /* set fake sockname Port Address */ if (len == 0) { desc->name_ptr = NULL; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_address(desc->sfamily, &desc->name_addr, - buf, &len) == NULL) + else if (inet_set_faddress + (desc->sfamily, &desc->name_addr, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); else { desc->name_ptr = &desc->name_addr; + desc->name_addr_len = (SOCKLEN_T) len; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } @@ -8877,7 +8920,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, case INET_REQ_BIND: { /* bind socket */ char tbuf[2]; inet_address local; - short port; + int port; DEBUGF(("inet_ctl(%ld): BIND\r\n", (long)desc->port)); @@ -8894,13 +8937,14 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, desc->state = INET_STATE_BOUND; - if ((port = inet_address_port(&local)) == 0) { + port = inet_address_port(&local); + if (port == 0) { SOCKLEN_T adrlen = sizeof(local); sock_name(desc->s, &local.sa, &adrlen); port = inet_address_port(&local); } - port = sock_ntohs(port); - put_int16(port, tbuf); + else if (port == -1) port = 0; + put_int16(sock_ntohs((Uint16) port), tbuf); return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } @@ -9469,8 +9513,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, timeout = get_int32(buf); buf += 4; len -= 4; - if (inet_set_address(desc->inet.sfamily, &desc->inet.remote, - buf, &len) == NULL) + if (inet_set_faddress + (desc->inet.sfamily, &desc->inet.remote, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); code = sock_connect(desc->inet.s, @@ -11528,6 +11572,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, case INET_AF_INET6: return ctl_xerror("eafnosupport", rbuf, rsize); break; +#endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: af = AF_LOCAL; break; #endif default: return ctl_error(EINVAL, rbuf, rsize); @@ -11611,7 +11658,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* For SCTP, we do not set the peer's addr in desc->remote, as multiple peers are possible: */ - if (inet_set_address(desc->sfamily, &remote, buf, &len) == NULL) + if (inet_set_faddress(desc->sfamily, &remote, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); sock_select(desc, FD_CONNECT, 1); @@ -11651,8 +11698,8 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* Ignore timeout */ buf += 4; len -= 4; - if (inet_set_address(desc->sfamily, - &desc->remote, buf, &len) == NULL) + if (inet_set_faddress + (desc->sfamily, &desc->remote, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); code = sock_connect(desc->s, @@ -11832,12 +11879,12 @@ static void packet_inet_timeout(ErlDrvData e) /* THIS IS A "send*" REQUEST; on the Erlang side: "port_command". -** input should be: P1 P0 Address buffer . +** input should be: Family Address buffer . ** For UDP, buffer (after Address) is just data to be sent. ** For SCTP, buffer contains a list representing 2 items: ** (1) 6 parms for sctp_sndrcvinfo, as in sctp_get_sendparams(); ** (2) 0+ real data bytes. -** There is no destination address -- SCTYP send is performed over +** There is no destination address -- SCTP send is performed over ** an existing association, using "sctp_sndrcvinfo" specified. */ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) @@ -11912,7 +11959,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) /* UDP socket. Even if it is connected, there is an address prefix here -- ignored for connected sockets: */ sz = len; - qtr = inet_set_address(desc->sfamily, &other, ptr, &sz); + qtr = inet_set_faddress(desc->sfamily, &other, ptr, &sz); if (qtr == NULL) { inet_reply_error(desc, EINVAL); return; @@ -12090,7 +12137,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) inet_input_count(desc, n); udesc->i_ptr += n; - inet_family_get_address(desc, abuf, &other, &len); + inet_get_address(abuf, &other, &len); /* Copy formatted address to the buffer allocated; "len" is the actual length which must be <= than the original reserved. This means that the addr + data in the buffer are contiguous, @@ -12624,9 +12671,8 @@ int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port) if (!inet_set_address(AF_INET, &addr, buf, &blen)) return 0; - if (IS_SOCKET_ERROR(sock_connect(s, - (struct sockaddr *) &addr, - sizeof(struct sockaddr_in)))) + if (IS_SOCKET_ERROR + (sock_connect(s, (struct sockaddr *) &addr, blen))) return 0; return 1; } -- cgit v1.2.3