diff --git a/CHANGELOG b/CHANGELOG index b49fc59a..01390d71 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -147,6 +147,12 @@ HISTORY ++ Bugfixes: + 2015-01-17: Simon Goldschmidt + * sockets.c/.h, memp_std.h: fixed bug #40788 "lwip_setsockopt_internal() crashes" + by rewriting set/getsockopt functions to combine checks with the actual code + and add more NULL checks; this also fixes that CORE_LOCKING used message + passing for set/getsockopt. + 2014-12-19: Simon Goldschmidt * opt.h, dhcp.h/.c: prevent dhcp from starting when netif link is down (only when LWIP_DHCP_CHECK_LINK_UP==1, which is disabled by default for diff --git a/src/api/sockets.c b/src/api/sockets.c index ea6d3281..1c06e740 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -118,6 +118,18 @@ #define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0) +#define LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype) do { if((optlen) < sizeof(opttype)) { return EINVAL; }}while(0) +#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \ + LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \ + if ((sock)->conn == NULL) { return EINVAL; } }while(0) +#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \ + LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \ + if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { return EINVAL; } }while(0) +#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \ + if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { return ENOPROTOOPT; } }while(0) + + #define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name) API_VAR_REF(name) #define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name) #define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name) API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name) @@ -132,6 +144,18 @@ #define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) #endif /* LWIP_MPU_COMPATIBLE */ +#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD +#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int +#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val)) +#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((s32_t)*(int*)(optval)) +#else +#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval +#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \ + s32_t loc = (val); \ + ((struct timeval *)(optval))->tv_sec = (loc) / 1000U; \ + ((struct timeval *)(optval))->tv_sec = ((loc) % 1000U) * 1000U; }while(0) +#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((struct timeval *)(optval))->tv_sec * 1000U) + (((struct timeval *)(optval))->tv_usec / 1000U)) +#endif #define NUM_SOCKETS MEMP_NUM_NETCONN @@ -253,8 +277,12 @@ static const int err_to_errno_table[] = { /* Forward declaration of some functions */ static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); -static void lwip_getsockopt_internal(void *arg); -static void lwip_setsockopt_internal(void *arg); +#if !LWIP_TCPIP_CORE_LOCKING +static void lwip_getsockopt_callback(void *arg); +static void lwip_setsockopt_callback(void *arg); +#endif +static u8_t lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen); +static u8_t lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen); /** * Map a externally used socket index to the internal socket representation. @@ -1536,9 +1564,11 @@ lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) int lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) { - u8_t err = 0; + u8_t err; struct lwip_sock *sock = get_socket(s); +#if !LWIP_TCPIP_CORE_LOCKING LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data); +#endif /* !LWIP_TCPIP_CORE_LOCKING */ if (!sock) { return -1; @@ -1549,273 +1579,84 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) return -1; } - /* Do length and type checks for the various options first, to keep it readable. */ - switch (level) { - -/* Level: SOL_SOCKET */ - case SOL_SOCKET: - switch (optname) { - - case SO_ACCEPTCONN: - case SO_BROADCAST: - /* UNIMPL case SO_DEBUG: */ - /* UNIMPL case SO_DONTROUTE: */ - case SO_ERROR: - case SO_KEEPALIVE: - /* UNIMPL case SO_CONTIMEO: */ -#if LWIP_SO_RCVBUF - case SO_RCVBUF: -#endif /* LWIP_SO_RCVBUF */ - /* UNIMPL case SO_OOBINLINE: */ - /* UNIMPL case SO_SNDBUF: */ - /* UNIMPL case SO_RCVLOWAT: */ - /* UNIMPL case SO_SNDLOWAT: */ -#if SO_REUSE - case SO_REUSEADDR: - case SO_REUSEPORT: -#endif /* SO_REUSE */ - case SO_TYPE: - /* UNIMPL case SO_USELOOPBACK: */ - if (*optlen < sizeof(int)) { - err = EINVAL; - } - break; -#if LWIP_SO_SNDTIMEO || LWIP_SO_RCVTIMEO -#if LWIP_SO_SNDTIMEO - case SO_SNDTIMEO: -#endif /* LWIP_SO_SNDTIMEO */ -#if LWIP_SO_RCVTIMEO - case SO_RCVTIMEO: -#endif /* LWIP_SO_RCVTIMEO */ - if (*optlen < -#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD - sizeof(int) -#else /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - sizeof(struct timeval) -#endif /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD*/ - ) { - err = EINVAL; - } - break; -#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_RCVTIMEO */ +#if LWIP_TCPIP_CORE_LOCKING + /* core-locking can just call the -impl function */ + LOCK_TCPIP_CORE(); + err = lwip_getsockopt_impl(s, level, optname, optval, optlen); + UNLOCK_TCPIP_CORE(); - case SO_NO_CHECK: - if (*optlen < sizeof(int)) { - err = EINVAL; - } -#if LWIP_UDP - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP || - ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { - /* this flag is only available for UDP, not for UDP lite */ - err = EAFNOSUPPORT; - } -#endif /* LWIP_UDP */ - break; +#else /* LWIP_TCPIP_CORE_LOCKING */ - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; - -/* Level: IPPROTO_IP */ - case IPPROTO_IP: - switch (optname) { - /* UNIMPL case IP_HDRINCL: */ - /* UNIMPL case IP_RCVDSTADDR: */ - /* UNIMPL case IP_RCVIF: */ - case IP_TTL: - case IP_TOS: - if (*optlen < sizeof(int)) { - err = EINVAL; - } - break; -#if LWIP_IGMP - case IP_MULTICAST_TTL: - if (*optlen < sizeof(u8_t)) { - err = EINVAL; - } - break; - case IP_MULTICAST_IF: - if (*optlen < sizeof(struct in_addr)) { - err = EINVAL; - } - break; - case IP_MULTICAST_LOOP: - if (*optlen < sizeof(u8_t)) { - err = EINVAL; - } - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { - err = EAFNOSUPPORT; - } - break; -#endif /* LWIP_IGMP */ - - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; - -#if LWIP_TCP -/* Level: IPPROTO_TCP */ - case IPPROTO_TCP: - if (*optlen < sizeof(int)) { - err = EINVAL; - break; - } - - /* If this is no TCP socket, ignore any options. */ - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) - return 0; - - switch (optname) { - case TCP_NODELAY: - case TCP_KEEPALIVE: -#if LWIP_TCP_KEEPALIVE - case TCP_KEEPIDLE: - case TCP_KEEPINTVL: - case TCP_KEEPCNT: -#endif /* LWIP_TCP_KEEPALIVE */ - break; - - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -#endif /* LWIP_TCP */ - -#if LWIP_IPV6 -/* Level: IPPROTO_IPV6 */ - case IPPROTO_IPV6: - switch (optname) { - case IPV6_V6ONLY: - if (*optlen < sizeof(int)) { - err = EINVAL; - } - /* @todo: this does not work for datagram sockets, yet */ - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) - return 0; - break; - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -#endif /* LWIP_IPV6 */ - -#if LWIP_UDP && LWIP_UDPLITE -/* Level: IPPROTO_UDPLITE */ - case IPPROTO_UDPLITE: - if (*optlen < sizeof(int)) { - err = EINVAL; - break; - } - - /* If this is no UDP lite socket, ignore any options. */ - if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { - return 0; - } - - switch (optname) { - case UDPLITE_SEND_CSCOV: - case UDPLITE_RECV_CSCOV: - break; - - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -#endif /* LWIP_UDP && LWIP_UDPLITE*/ - /* Level: IPPROTO_RAW */ - case IPPROTO_RAW: - switch (optname) { -#if LWIP_IPV6 - case IPV6_CHECKSUM: - if (*optlen < sizeof(int)) { - err = EINVAL; - break; - } -#endif /* LWIP_IPV6 */ - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -/* UNDEFINED LEVEL */ - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", - s, level, optname)); - err = ENOPROTOOPT; - } /* switch */ - - - if (err != 0) { - sock_set_errno(sock, err); +#if LWIP_MPU_COMPATIBLE + /* MPU_COMPATIBLE copies the optval data, so check for max size here */ + if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) { + sock_set_errno(sock, ENOBUFS); return -1; } +#endif /* LWIP_MPU_COMPATIBLE */ - /* Now do the actual option processing */ LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock); - LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).sock = sock; -#ifdef LWIP_DEBUG LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s; -#endif /* LWIP_DEBUG */ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level; LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen; +#if !LWIP_MPU_COMPATIBLE LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval = optval; - LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen; - LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = err; -#if LWIP_TCPIP_CORE_LOCKING - LOCK_TCPIP_CORE(); - lwip_getsockopt_internal(&LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); - UNLOCK_TCPIP_CORE(); -#else /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* !LWIP_MPU_COMPATIBLE */ + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0; #if LWIP_NETCONN_SEM_PER_THREAD LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); #else LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed; #endif - tcpip_callback(lwip_getsockopt_internal, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); + tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0); -#endif /* LWIP_TCPIP_CORE_LOCKING */ + + /* write back optlen and optval */ + *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen; +#if LWIP_MPU_COMPATIBLE + memcpy(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen); +#endif /* LWIP_MPU_COMPATIBLE */ + /* maybe lwip_getsockopt_internal has changed err */ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err; LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); +#endif /* LWIP_TCPIP_CORE_LOCKING */ sock_set_errno(sock, err); return err ? -1 : 0; } +#if !LWIP_TCPIP_CORE_LOCKING +/** lwip_getsockopt_callback: only used without CORE_LOCKING + * to get into the tcpip_thread + */ static void -lwip_getsockopt_internal(void *arg) +lwip_getsockopt_callback(void *arg) { - struct lwip_sock *sock; -#ifdef LWIP_DEBUG - int s; -#endif /* LWIP_DEBUG */ - int level, optname; - void *optval; struct lwip_setgetsockopt_data *data; - LWIP_ASSERT("arg != NULL", arg != NULL); - data = (struct lwip_setgetsockopt_data*)arg; - sock = data->sock; -#ifdef LWIP_DEBUG - s = data->s; -#endif /* LWIP_DEBUG */ - level = data->level; - optname = data->optname; - optval = data->optval; + + data->err = lwip_getsockopt_impl(data->s, data->level, data->optname, data->optval, + &data->optlen); + + sys_sem_signal((sys_sem_t*)(data->completed_sem)); +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** lwip_getsockopt_impl: the actual implementation of getsockopt: + * same argument as lwip_getsockopt, either called directly or through callback + */ +static u8_t +lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + u8_t err = 0; + struct lwip_sock *sock = tryget_socket(s); + if (!sock) { + return EBADF; + } switch (level) { @@ -1824,34 +1665,31 @@ lwip_getsockopt_internal(void *arg) switch (optname) { case SO_ACCEPTCONN: - if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_TCP) { - if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) { - *(int*)optval = 1; - } else { - *(int*)optval = 0; - } + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) { + return ENOPROTOOPT; + } + if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) { + *(int*)optval = 1; } else { - data->err = ENOPROTOOPT; + *(int*)optval = 0; } break; /* The option flags */ case SO_BROADCAST: - /* UNIMPL case SO_DEBUG: */ - /* UNIMPL case SO_DONTROUTE: */ case SO_KEEPALIVE: - /* UNIMPL case SO_OOBINCLUDE: */ #if SO_REUSE case SO_REUSEADDR: - case SO_REUSEPORT: #endif /* SO_REUSE */ - /*case SO_USELOOPBACK: UNIMPL */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", s, optname, (*(int*)optval?"on":"off"))); break; case SO_TYPE: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) { case NETCONN_RAW: *(int*)optval = SOCK_RAW; @@ -1873,10 +1711,11 @@ lwip_getsockopt_internal(void *arg) break; case SO_ERROR: + LWIP_SOCKOPT_CHECK_OPTLEN(*optlen, int); /* only overwrite ERR_OK or temporary errors */ - if ((sock->err == 0) || (sock->err == EINPROGRESS)) { + if (((sock->err == 0) || (sock->err == EINPROGRESS)) && (sock->conn != NULL)) { sock_set_errno(sock, err_to_errno(sock->conn->last_err)); - } + } *(int *)optval = sock->err; sock->err = 0; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", @@ -1885,42 +1724,38 @@ lwip_getsockopt_internal(void *arg) #if LWIP_SO_SNDTIMEO case SO_SNDTIMEO: - { - s32_t val = netconn_get_sendtimeout(sock->conn); -#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD - *(int *)optval = val; -#else /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - ((struct timeval *)optval)->tv_sec = val / 1000U; - ((struct timeval *)optval)->tv_sec = (val % 1000U) * 1000U; -#endif /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - } + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn)); break; #endif /* LWIP_SO_SNDTIMEO */ #if LWIP_SO_RCVTIMEO case SO_RCVTIMEO: - { - s32_t val = netconn_get_recvtimeout(sock->conn); -#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD - *(int *)optval = (int)val; -#else /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - ((struct timeval *)optval)->tv_sec = val / 1000U; - ((struct timeval *)optval)->tv_sec = (val % 1000U) * 1000U; -#endif /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - } + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn)); break; #endif /* LWIP_SO_RCVTIMEO */ #if LWIP_SO_RCVBUF case SO_RCVBUF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); *(int *)optval = netconn_get_recvbufsize(sock->conn); break; #endif /* LWIP_SO_RCVBUF */ #if LWIP_UDP case SO_NO_CHECK: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP); +#if LWIP_UDPLITE + if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) { + /* this flag is only available for UDP, not for UDP lite */ + return EAFNOSUPPORT; + } +#endif /* LWIP_UDPLITE */ *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; break; #endif /* LWIP_UDP*/ default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -1929,27 +1764,38 @@ lwip_getsockopt_internal(void *arg) case IPPROTO_IP: switch (optname) { case IP_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); *(int*)optval = sock->conn->pcb.ip->ttl; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", s, *(int *)optval)); break; case IP_TOS: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); *(int*)optval = sock->conn->pcb.ip->tos; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", s, *(int *)optval)); break; #if LWIP_IGMP case IP_MULTICAST_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + return ENOPROTOOPT; + } *(u8_t*)optval = sock->conn->pcb.ip->ttl; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n", s, *(int *)optval)); break; case IP_MULTICAST_IF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + return ENOPROTOOPT; + } inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n", s, *(u32_t *)optval)); break; case IP_MULTICAST_LOOP: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t); if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) { *(u8_t*)optval = 1; } else { @@ -1960,7 +1806,9 @@ lwip_getsockopt_internal(void *arg) break; #endif /* LWIP_IGMP */ default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -1968,6 +1816,8 @@ lwip_getsockopt_internal(void *arg) #if LWIP_TCP /* Level: IPPROTO_TCP */ case IPPROTO_TCP: + /* Special case: all IPPROTO_TCP option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP); switch (optname) { case TCP_NODELAY: *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp); @@ -1976,29 +1826,31 @@ lwip_getsockopt_internal(void *arg) break; case TCP_KEEPALIVE: *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n", s, *(int *)optval)); break; #if LWIP_TCP_KEEPALIVE case TCP_KEEPIDLE: *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n", + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n", s, *(int *)optval)); break; case TCP_KEEPINTVL: *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000); - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n", + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n", s, *(int *)optval)); break; case TCP_KEEPCNT: *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt; - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n", + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n", s, *(int *)optval)); break; #endif /* LWIP_TCP_KEEPALIVE */ default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -2009,12 +1861,19 @@ lwip_getsockopt_internal(void *arg) case IPPROTO_IPV6: switch (optname) { case IPV6_V6ONLY: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); + /* @todo: this does not work for datagram sockets, yet */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + return ENOPROTOOPT; + } *(int*)optval = ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n", s, *(int *)optval)); break; default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -2023,6 +1882,12 @@ lwip_getsockopt_internal(void *arg) #if LWIP_UDP && LWIP_UDPLITE /* Level: IPPROTO_UDPLITE */ case IPPROTO_UDPLITE: + /* Special case: all IPPROTO_UDPLITE option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { + return ENOPROTOOPT; + } switch (optname) { case UDPLITE_SEND_CSCOV: *(int*)optval = sock->conn->pcb.udp->chksum_len_tx; @@ -2035,7 +1900,9 @@ lwip_getsockopt_internal(void *arg) s, (*(int*)optval)) ); break; default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -2045,6 +1912,7 @@ lwip_getsockopt_internal(void *arg) switch (optname) { #if LWIP_IPV6 case IPV6_CHECKSUM: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW); if (sock->conn->pcb.raw->chksum_reqd == 0) { *(int *)optval = -1; } else { @@ -2055,25 +1923,30 @@ lwip_getsockopt_internal(void *arg) break; #endif /* LWIP_IPV6 */ default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; default: - LWIP_ASSERT("unhandled level", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; break; } /* switch (level) */ -#if !LWIP_TCPIP_CORE_LOCKING - sys_sem_signal((sys_sem_t*)(data->completed_sem)); -#endif + + return err; } int lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) { - struct lwip_sock *sock = get_socket(s); u8_t err = 0; + struct lwip_sock *sock = get_socket(s); +#if !LWIP_TCPIP_CORE_LOCKING LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data); +#endif /* !LWIP_TCPIP_CORE_LOCKING */ if (!sock) { return -1; @@ -2084,301 +1957,79 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt return -1; } - /* Do length and type checks for the various options first, to keep it readable. */ - switch (level) { +#if LWIP_TCPIP_CORE_LOCKING + /* core-locking can just call the -impl function */ + LOCK_TCPIP_CORE(); + err = lwip_setsockopt_impl(s, level, optname, optval, optlen); + UNLOCK_TCPIP_CORE(); -/* Level: SOL_SOCKET */ - case SOL_SOCKET: - switch (optname) { +#else /* LWIP_TCPIP_CORE_LOCKING */ - case SO_BROADCAST: - /* UNIMPL case SO_DEBUG: */ - /* UNIMPL case SO_DONTROUTE: */ - case SO_KEEPALIVE: - /* UNIMPL case case SO_CONTIMEO: */ -#if LWIP_SO_RCVBUF - case SO_RCVBUF: -#endif /* LWIP_SO_RCVBUF */ - /* UNIMPL case SO_OOBINLINE: */ - /* UNIMPL case SO_SNDBUF: */ - /* UNIMPL case SO_RCVLOWAT: */ - /* UNIMPL case SO_SNDLOWAT: */ -#if SO_REUSE - case SO_REUSEADDR: - case SO_REUSEPORT: -#endif /* SO_REUSE */ - /* UNIMPL case SO_USELOOPBACK: */ - if (optlen < sizeof(int)) { - err = EINVAL; - } - break; -#if LWIP_SO_SNDTIMEO || LWIP_SO_RCVTIMEO -#if LWIP_SO_SNDTIMEO - case SO_SNDTIMEO: -#endif /* LWIP_SO_SNDTIMEO */ -#if LWIP_SO_RCVTIMEO - case SO_RCVTIMEO: -#endif /* LWIP_SO_RCVTIMEO */ - if (optlen < -#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD - sizeof(int) -#else /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - sizeof(struct timeval) -#endif /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD*/ - ) { - err = EINVAL; - } - break; -#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_RCVTIMEO */ - case SO_NO_CHECK: - if (optlen < sizeof(int)) { - err = EINVAL; - } -#if LWIP_UDP - if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) || - ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { - /* this flag is only available for UDP, not for UDP lite */ - err = EAFNOSUPPORT; - } -#endif /* LWIP_UDP */ - break; - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; - -/* Level: IPPROTO_IP */ - case IPPROTO_IP: - switch (optname) { - /* UNIMPL case IP_HDRINCL: */ - /* UNIMPL case IP_RCVDSTADDR: */ - /* UNIMPL case IP_RCVIF: */ - case IP_TTL: - case IP_TOS: - if (optlen < sizeof(int)) { - err = EINVAL; - } - break; -#if LWIP_IGMP - case IP_MULTICAST_TTL: - if (optlen < sizeof(u8_t)) { - err = EINVAL; - } - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { - err = EAFNOSUPPORT; - } - break; - case IP_MULTICAST_IF: - if (optlen < sizeof(struct in_addr)) { - err = EINVAL; - } - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { - err = EAFNOSUPPORT; - } - break; - case IP_MULTICAST_LOOP: - if (optlen < sizeof(u8_t)) { - err = EINVAL; - } - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { - err = EAFNOSUPPORT; - } - break; - case IP_ADD_MEMBERSHIP: - case IP_DROP_MEMBERSHIP: - if (optlen < sizeof(struct ip_mreq)) { - err = EINVAL; - } - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { - err = EAFNOSUPPORT; - } - break; -#endif /* LWIP_IGMP */ - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; - -#if LWIP_TCP -/* Level: IPPROTO_TCP */ - case IPPROTO_TCP: - if (optlen < sizeof(int)) { - err = EINVAL; - break; - } - - /* If this is no TCP socket, ignore any options. */ - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) - return 0; - - switch (optname) { - case TCP_NODELAY: - case TCP_KEEPALIVE: -#if LWIP_TCP_KEEPALIVE - case TCP_KEEPIDLE: - case TCP_KEEPINTVL: - case TCP_KEEPCNT: -#endif /* LWIP_TCP_KEEPALIVE */ - break; - - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -#endif /* LWIP_TCP */ - -#if LWIP_IPV6 -/* Level: IPPROTO_IPV6 */ - case IPPROTO_IPV6: - switch (optname) { - case IPV6_V6ONLY: - if (optlen < sizeof(int)) { - err = EINVAL; - } - - /* @todo: this does not work for datagram sockets, yet */ - if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { - return 0; - } - - break; - case IPV6_CHECKSUM: - err = EINVAL; - break; - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; - case IPPROTO_ICMPV6: - switch (optname) { - case IPV6_CHECKSUM: - err = EINVAL; - break; - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_ICMPV6, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -#endif /* LWIP_IPV6 */ - -#if LWIP_UDP && LWIP_UDPLITE -/* Level: IPPROTO_UDPLITE */ - case IPPROTO_UDPLITE: - if (optlen < sizeof(int)) { - err = EINVAL; - break; - } - - /* If this is no UDP lite socket, ignore any options. */ - if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { - return 0; - } - - switch (optname) { - case UDPLITE_SEND_CSCOV: - case UDPLITE_RECV_CSCOV: - break; - - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -#endif /* LWIP_UDP && LWIP_UDPLITE */ -/* Level: IPPROTO_RAW */ - case IPPROTO_RAW: - switch (optname) { -#if LWIP_IPV6 - case IPV6_CHECKSUM: - /* Per RFC3542, odd offsets are not allowed */ - if ((*(int *)optval > 0) && (*(int *)optval & 1)) { - err = EINVAL; - } - break; -#endif /* LWIP_IPV6 */ - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", - s, optname)); - err = ENOPROTOOPT; - } /* switch (optname) */ - break; -/* UNDEFINED LEVEL */ - default: - LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", - s, level, optname)); - err = ENOPROTOOPT; - } /* switch (level) */ - - - if (err != 0) { - sock_set_errno(sock, err); +#if LWIP_MPU_COMPATIBLE + /* MPU_COMPATIBLE copies the optval data, so check for max size here */ + if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) { + sock_set_errno(sock, ENOBUFS); return -1; } +#endif /* LWIP_MPU_COMPATIBLE */ - - /* Now do the actual option processing */ LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock); - LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).sock = sock; -#ifdef LWIP_DEBUG LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s; -#endif /* LWIP_DEBUG */ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level; LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen; +#if LWIP_MPU_COMPATIBLE + memcpy(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen); +#else /* LWIP_MPU_COMPATIBLE */ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval = (void*)optval; - LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = &optlen; - LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = err; -#if LWIP_TCPIP_CORE_LOCKING - LOCK_TCPIP_CORE(); - lwip_setsockopt_internal(&LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); - UNLOCK_TCPIP_CORE(); -#else /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_MPU_COMPATIBLE */ + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0; #if LWIP_NETCONN_SEM_PER_THREAD LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); #else LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed; #endif - tcpip_callback(lwip_setsockopt_internal, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); + tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0); -#endif /* LWIP_TCPIP_CORE_LOCKING */ - /* maybe lwip_setsockopt_internal has changed err */ + + /* maybe lwip_getsockopt_internal has changed err */ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err; LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); +#endif /* LWIP_TCPIP_CORE_LOCKING */ sock_set_errno(sock, err); return err ? -1 : 0; } +#if !LWIP_TCPIP_CORE_LOCKING +/** lwip_setsockopt_callback: only used without CORE_LOCKING + * to get into the tcpip_thread + */ static void -lwip_setsockopt_internal(void *arg) +lwip_setsockopt_callback(void *arg) { - struct lwip_sock *sock; -#ifdef LWIP_DEBUG - int s; -#endif /* LWIP_DEBUG */ - int level, optname; - const void *optval; struct lwip_setgetsockopt_data *data; - LWIP_ASSERT("arg != NULL", arg != NULL); - data = (struct lwip_setgetsockopt_data*)arg; - sock = data->sock; -#ifdef LWIP_DEBUG - s = data->s; -#endif /* LWIP_DEBUG */ - level = data->level; - optname = data->optname; - optval = data->optval; + + data->err = lwip_setsockopt_impl(data->s, data->level, data->optname, data->optval, + data->optlen); + + sys_sem_signal((sys_sem_t*)(data->completed_sem)); +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** lwip_setsockopt_impl: the actual implementation of setsockopt: + * same argument as lwip_setsockopt, either called directly or through callback + */ +static u8_t +lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + u8_t err = 0; + struct lwip_sock *sock = tryget_socket(s); + if (!sock) { + return EBADF; + } switch (level) { @@ -2386,17 +2037,15 @@ lwip_setsockopt_internal(void *arg) case SOL_SOCKET: switch (optname) { + /* SO_ACCEPTCONN is get-only */ + /* The option flags */ case SO_BROADCAST: - /* UNIMPL case SO_DEBUG: */ - /* UNIMPL case SO_DONTROUTE: */ case SO_KEEPALIVE: - /* UNIMPL case SO_OOBINCLUDE: */ #if SO_REUSE case SO_REUSEADDR: - case SO_REUSEPORT: #endif /* SO_REUSE */ - /* UNIMPL case SO_USELOOPBACK: */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); if (*(int*)optval) { ip_set_option(sock->conn->pcb.ip, optname); } else { @@ -2405,39 +2054,37 @@ lwip_setsockopt_internal(void *arg) LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", s, optname, (*(int*)optval?"on":"off"))); break; + + /* SO_TYPE is get-only */ + /* SO_ERROR is get-only */ + #if LWIP_SO_SNDTIMEO case SO_SNDTIMEO: - { - s32_t val; -#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD - val = (s32_t)*(int*)optval; -#else /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - val = (((struct timeval *)optval)->tv_sec * 1000U) + (((struct timeval *)optval)->tv_usec / 1000U); -#endif /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - netconn_set_sendtimeout(sock->conn, val); - } + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + netconn_set_sendtimeout(sock->conn, LWIP_SO_SNDRCVTIMEO_GET_MS(optval)); break; #endif /* LWIP_SO_SNDTIMEO */ #if LWIP_SO_RCVTIMEO case SO_RCVTIMEO: - { - s32_t val; -#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD - val = (s32_t)*(int*)optval; -#else /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - val = (((struct timeval *)optval)->tv_sec * 1000U) + (((struct timeval *)optval)->tv_usec / 1000U); -#endif /* LWIP_SO_SNDRCVTIMEO_NONSTANDARD */ - netconn_set_recvtimeout(sock->conn, (int)val); - } + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + netconn_set_recvtimeout(sock->conn, (int)LWIP_SO_SNDRCVTIMEO_GET_MS(optval)); break; #endif /* LWIP_SO_RCVTIMEO */ #if LWIP_SO_RCVBUF case SO_RCVBUF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int); netconn_set_recvbufsize(sock->conn, *(int*)optval); break; #endif /* LWIP_SO_RCVBUF */ #if LWIP_UDP case SO_NO_CHECK: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP); +#if LWIP_UDPLITE + if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) { + /* this flag is only available for UDP, not for UDP lite */ + return EAFNOSUPPORT; + } +#endif /* LWIP_UDPLITE */ if (*(int*)optval) { udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM); } else { @@ -2446,7 +2093,9 @@ lwip_setsockopt_internal(void *arg) break; #endif /* LWIP_UDP */ default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -2455,23 +2104,28 @@ lwip_setsockopt_internal(void *arg) case IPPROTO_IP: switch (optname) { case IP_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n", s, sock->conn->pcb.ip->ttl)); break; case IP_TOS: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval); LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n", s, sock->conn->pcb.ip->tos)); break; #if LWIP_IGMP case IP_MULTICAST_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP); sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval); break; case IP_MULTICAST_IF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP); inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval); break; case IP_MULTICAST_LOOP: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP); if (*(u8_t*)optval) { udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP); } else { @@ -2482,25 +2136,29 @@ lwip_setsockopt_internal(void *arg) case IP_DROP_MEMBERSHIP: { /* If this is a TCP or a RAW socket, ignore these options. */ - err_t err; + /* @todo: assign membership to this socket so that it is dropped when closing the socket */ + err_t igmp_err; struct ip_mreq *imr = (struct ip_mreq *)optval; ip_addr_t if_addr; ip_addr_t multi_addr; + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP); inet_addr_to_ipaddr(&if_addr, &imr->imr_interface); inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr); if(optname == IP_ADD_MEMBERSHIP){ - err = igmp_joingroup(&if_addr, &multi_addr); + igmp_err = igmp_joingroup(&if_addr, &multi_addr); } else { - err = igmp_leavegroup(&if_addr, &multi_addr); + igmp_err = igmp_leavegroup(&if_addr, &multi_addr); } - if(err != ERR_OK) { - data->err = EADDRNOTAVAIL; + if (igmp_err != ERR_OK) { + err = EADDRNOTAVAIL; } } break; #endif /* LWIP_IGMP */ default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -2508,6 +2166,8 @@ lwip_setsockopt_internal(void *arg) #if LWIP_TCP /* Level: IPPROTO_TCP */ case IPPROTO_TCP: + /* Special case: all IPPROTO_TCP option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP); switch (optname) { case TCP_NODELAY: if (*(int*)optval) { @@ -2542,7 +2202,9 @@ lwip_setsockopt_internal(void *arg) break; #endif /* LWIP_TCP_KEEPALIVE */ default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -2553,6 +2215,8 @@ lwip_setsockopt_internal(void *arg) case IPPROTO_IPV6: switch (optname) { case IPV6_V6ONLY: + /* @todo: this does not work for datagram sockets, yet */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP); if (*(int*)optval) { sock->conn->flags |= NETCONN_FLAG_IPV6_V6ONLY; } else { @@ -2562,7 +2226,9 @@ lwip_setsockopt_internal(void *arg) s, ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0))); break; default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -2571,6 +2237,12 @@ lwip_setsockopt_internal(void *arg) #if LWIP_UDP && LWIP_UDPLITE /* Level: IPPROTO_UDPLITE */ case IPPROTO_UDPLITE: + /* Special case: all IPPROTO_UDPLITE option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { + return ENOPROTOOPT; + } switch (optname) { case UDPLITE_SEND_CSCOV: if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) { @@ -2593,7 +2265,9 @@ lwip_setsockopt_internal(void *arg) s, (*(int*)optval)) ); break; default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; @@ -2603,8 +2277,12 @@ lwip_setsockopt_internal(void *arg) switch (optname) { #if LWIP_IPV6 case IPV6_CHECKSUM: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW); if (*(int *)optval < 0) { sock->conn->pcb.raw->chksum_reqd = 0; + } else if (*(int *)optval & 1) { + /* Per RFC3542, odd offsets are not allowed */ + return EINVAL; } else { sock->conn->pcb.raw->chksum_reqd = 1; sock->conn->pcb.raw->chksum_offset = *(int *)optval; @@ -2614,17 +2292,20 @@ lwip_setsockopt_internal(void *arg) break; #endif /* LWIP_IPV6 */ default: - LWIP_ASSERT("unhandled optname", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; break; } /* switch (optname) */ break; default: - LWIP_ASSERT("unhandled level", 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; break; } /* switch (level) */ -#if !LWIP_TCPIP_CORE_LOCKING - sys_sem_signal((sys_sem_t*)(data->completed_sem)); -#endif + + return err; } int diff --git a/src/include/lwip/memp_std.h b/src/include/lwip/memp_std.h index e12a7f9b..97872e13 100644 --- a/src/include/lwip/memp_std.h +++ b/src/include/lwip/memp_std.h @@ -63,7 +63,7 @@ LWIP_MEMPOOL(API_MSG, MEMP_NUM_API_MSG, sizeof(struct api_msg), #if LWIP_DNS LWIP_MEMPOOL(DNS_API_MSG, MEMP_NUM_DNS_API_MSG, sizeof(struct dns_api_msg), "DNS_API_MSG") #endif -#if LWIP_SOCKET +#if LWIP_SOCKET && !LWIP_TCPIP_CORE_LOCKING LWIP_MEMPOOL(SOCKET_SETGETSOCKOPT_DATA, MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA, sizeof(struct lwip_setgetsockopt_data), "SOCKET_SETGETSOCKOPT_DATA") #endif #if LWIP_NETIF_API diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h index 4c952e99..97897c89 100644 --- a/src/include/lwip/sockets.h +++ b/src/include/lwip/sockets.h @@ -108,31 +108,34 @@ typedef u32_t socklen_t; struct lwip_sock; +#if !LWIP_TCPIP_CORE_LOCKING +/** Maximum optlen used by setsockopt/getsockopt */ +#define LWIP_SETGETSOCKOPT_MAXOPTLEN 16 + /** This struct is used to pass data to the set/getsockopt_internal * functions running in tcpip_thread context (only a void* is allowed) */ struct lwip_setgetsockopt_data { - /** socket struct for which to change options */ - struct lwip_sock *sock; -#ifdef LWIP_DEBUG /** socket index for which to change options */ int s; -#endif /* LWIP_DEBUG */ /** level of the option to process */ int level; /** name of the option to process */ int optname; /** set: value to set the option to * get: value of the option is stored here */ - void *optval; +#if LWIP_MPU_COMPATIBLE + u8_t optval[LWIP_SETGETSOCKOPT_MAXOPTLEN]; +#else + void* optval; +#endif /** size of *optval */ - socklen_t *optlen; + socklen_t optlen; /** if an error occurs, it is temporarily stored here */ err_t err; -#if !LWIP_TCPIP_CORE_LOCKING /** semaphore to wake up the calling task */ void* completed_sem; -#endif /* !LWIP_TCPIP_CORE_LOCKING */ }; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ /* Socket protocol types (TCP/UDP/RAW) */ #define SOCK_STREAM 1