diff --git a/src/common.c b/src/common.c index 1929d987..a59717d9 100644 --- a/src/common.c +++ b/src/common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2015 Nikos Mavrogiannopoulos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -448,6 +448,85 @@ void *_talloc_size2(void *ctx, size_t size) return talloc_size(ctx, size); } +/* like recvfrom but also returns the address of our interface. + * + * @def_port: is provided to fill in the missing port number + * in our_addr. + */ +ssize_t oc_recvfrom_at(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen, + struct sockaddr *our_addr, socklen_t *our_addrlen, + int def_port) +{ +int ret; +char cmbuf[256]; +struct iovec iov = { buf, len }; +struct cmsghdr *cmsg; +struct msghdr mh = { + .msg_name = src_addr, + .msg_namelen = *addrlen, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmbuf, + .msg_controllen = sizeof(cmbuf), +}; + + ret = recvmsg(sockfd, &mh, 0); + if (ret < 0) { + return -1; + } + + /* find our address */ + for (cmsg = CMSG_FIRSTHDR(&mh); cmsg != NULL; cmsg = CMSG_NXTHDR(&mh, cmsg)) { +#if defined(IP_PKTINFO) + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pi = CMSG_DATA(cmsg); + struct sockaddr_in *a = (struct sockaddr_in*)our_addr; + + if (*our_addrlen < sizeof(struct sockaddr_in)) + return -1; + + a->sin_family = AF_INET; + memcpy(&a->sin_addr, &pi->ipi_addr, sizeof(struct in_addr)); + a->sin_port = htons(def_port); + *our_addrlen = sizeof(struct sockaddr_in); + break; + } +#elif defined(IP_RECVDSTADDR) + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { + struct in_addr *pi = CMSG_DATA(cmsg); + struct sockaddr_in *a = (struct sockaddr_in*)our_addr; + + if (*our_addrlen < sizeof(struct sockaddr_in)) + return -1; + + a->sin_family = AF_INET; + memcpy(&a->sin_addr, &pi->ipi_addr, sizeof(struct in_addr)); + a->sin_port = htons(def_port); + *our_addrlen = sizeof(struct sockaddr_in); + break; + } +#endif +#ifdef IPV6_RECVPKTINFO + if (cmsg->cmsg_level != IPPROTO_IPV6 || cmsg->cmsg_type != IPV6_RECVPKTINFO) { + struct in6_pktinfo *pi = CMSG_DATA(cmsg); + struct sockaddr_in6 *a = (struct sockaddr_in6*)our_addr; + + if (*our_addrlen < sizeof(struct sockaddr_in6)) + return -1; + + a->sin6_family = AF_INET6; + memcpy(&a->sin6_addr, &pi->ipi6_addr, sizeof(struct in6_addr)); + a->sin6_port = htons(def_port); + *our_addrlen = sizeof(struct sockaddr_in6); + break; + } +#endif + } + + return ret; +} + #ifndef HAVE_STRLCPY /* diff --git a/src/common.h b/src/common.h index a45d778c..0c876ba2 100644 --- a/src/common.h +++ b/src/common.h @@ -83,6 +83,11 @@ int recv_socket_msg(void *pool, int fd, uint8_t cmd, const char* cmd_request_to_str(unsigned cmd); +ssize_t oc_recvfrom_at(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen, + struct sockaddr *our_addr, socklen_t *our_addrlen, + int def_port); + inline static void safe_memset(void *data, int c, size_t size) { diff --git a/src/main.c b/src/main.c index c5c75876..8d392ef2 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2014 Nikos Mavrogiannopoulos + * Copyright (C) 2013-2015 Nikos Mavrogiannopoulos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -82,7 +82,7 @@ static void add_listener(void *pool, struct listen_list_st *list, tmp->family = family; tmp->sock_type = socktype; tmp->protocol = protocol; - + tmp->addr_len = addr_len; memcpy(&tmp->addr, addr, addr_len); @@ -199,7 +199,7 @@ int _listen_ports(void *pool, struct cfg_st* config, } set_common_socket_options(s); - + add_listener(pool, list, s, ptr->ai_family, ptr->ai_socktype==SOCK_STREAM?SOCK_TYPE_TCP:SOCK_TYPE_UDP, ptr->ai_protocol, ptr->ai_addr, ptr->ai_addrlen); @@ -344,7 +344,7 @@ listen_ports(void *pool, struct cfg_st* config, if (config->foreground != 0) fprintf(stderr, "listening on %d systemd sockets...\n", list->total); - + return 0; } #endif @@ -412,7 +412,7 @@ listen_ports(void *pool, struct cfg_st* config, if (ret < 0) { return -1; } - + freeaddrinfo(res); } @@ -463,7 +463,7 @@ struct script_wait_st *stmp = NULL, *spos; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { estatus = WEXITSTATUS(status); - + if (pid == s->sec_mod_pid) { mslog(s, NULL, LOG_ERR, "ocserv-secmod died unexpectedly"); terminate = 1; @@ -483,7 +483,7 @@ struct script_wait_st *stmp = NULL, *spos; break; } } - + if (WIFSIGNALED(status)) { if (WTERMSIG(status) == SIGSEGV) mslog(s, NULL, LOG_ERR, "Child %u died with sigsegv\n", (unsigned)pid); @@ -535,7 +535,7 @@ static void drop_privileges(main_server_st* s) (int) s->config->gid, strerror(e)); exit(1); } - + ret = setgroups(1, &s->config->gid); if (ret < 0) { e = errno; @@ -654,6 +654,7 @@ void request_reload(int signo) reload_conf = 1; } + /* A UDP fd will not be forwarded to worker process before this number of * seconds has passed. That is to prevent a duplicate message messing the worker. */ @@ -667,76 +668,29 @@ int ret, e; struct sockaddr_storage cli_addr; struct sockaddr_storage our_addr; struct proc_st *proc_to_send = NULL; -socklen_t cli_addr_size, our_addr_size = 0; +socklen_t cli_addr_size, our_addr_size; uint8_t buffer[1024]; -char cmbuf[256]; char tbuf[64]; uint8_t *session_id = NULL; int session_id_size = 0; ssize_t buffer_size; int match_ip_only = 0; time_t now; -struct iovec iov = { buffer, sizeof(buffer) }; -struct cmsghdr *cmsg; int sfd = -1; -struct msghdr mh = { - .msg_name = &cli_addr, - .msg_namelen = sizeof(cli_addr), - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cmbuf, - .msg_controllen = sizeof(cmbuf), -}; /* first receive from the correct client and connect socket */ cli_addr_size = sizeof(cli_addr); - ret = recvmsg(listener->fd, &mh, 0); + our_addr_size = sizeof(our_addr); + ret = oc_recvfrom_at(listener->fd, buffer, sizeof(buffer), 0, + (struct sockaddr*)&cli_addr, &cli_addr_size, + (struct sockaddr*)&our_addr, &our_addr_size, + s->config->udp_port); if (ret < 0) { mslog(s, NULL, LOG_INFO, "error receiving in UDP socket"); return -1; } - buffer_size = ret; - /* find our address */ - for (cmsg = CMSG_FIRSTHDR(&mh); cmsg != NULL; cmsg = CMSG_NXTHDR(&mh, cmsg)) { -#if defined(IP_PKTINFO) - if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { - struct in_pktinfo *pi = CMSG_DATA(cmsg); - struct sockaddr_in *a = (struct sockaddr_in*)&our_addr; - - a->sin_family = AF_INET; - memcpy(&a->sin_addr, &pi->ipi_addr, sizeof(struct in_addr)); - a->sin_port = htons(s->config->udp_port); - our_addr_size = sizeof(struct sockaddr_in); - break; - } -#elif defined(IP_RECVDSTADDR) - if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { - struct in_addr *pi = CMSG_DATA(cmsg); - struct sockaddr_in *a = (struct sockaddr_in*)&our_addr; - - a->sin_family = AF_INET; - memcpy(&a->sin_addr, &pi->ipi_addr, sizeof(struct in_addr)); - a->sin_port = htons(s->config->udp_port); - our_addr_size = sizeof(struct sockaddr_in); - break; - } -#endif -#ifdef IPV6_RECVPKTINFO - if (cmsg->cmsg_level != IPPROTO_IPV6 || cmsg->cmsg_type != IPV6_RECVPKTINFO) { - struct in6_pktinfo *pi = CMSG_DATA(cmsg); - struct sockaddr_in6 *a = (struct sockaddr_in6*)&our_addr; - - a->sin6_family = AF_INET6; - memcpy(&a->sin6_addr, &pi->ipi6_addr, sizeof(struct in6_addr)); - a->sin6_port = htons(s->config->udp_port); - our_addr_size = sizeof(struct sockaddr_in6); - break; - } -#endif - } - /* obtain the session id */ if (buffer_size < RECORD_PAYLOAD_POS+HANDSHAKE_SESSION_ID_POS+GNUTLS_MAX_SESSION_ID+2) { mslog(s, NULL, LOG_INFO, "%s: too short UDP packet", @@ -1040,7 +994,7 @@ int main(int argc, char** argv) #ifdef HAVE_LIBWRAP allow_severity = LOG_DAEMON|LOG_INFO; deny_severity = LOG_DAEMON|LOG_WARNING; -#endif +#endif if (s->config->foreground == 0) { if (daemon(0, 0) == -1) { @@ -1051,7 +1005,7 @@ int main(int argc, char** argv) } write_pid_file(); - + run_sec_mod(s); ret = ctl_handler_init(s);