On 14.02.2012 21:34, Christof Mroz wrote:
+/** > + * Receive data from the peer associated with a socket. > + * Waits for base exchange if no host association exists. > + * > + * @note Data is currently sent unencrypted. > + * > + * @param fd file descriptor of the socket to receive from > + * @param buf buffer for received data > + * @param len size of buf > + * @param flags recvfrom() flags > + * @param addr buffer for the associated peer HIT > + * @param addr_len size of dst_hit > + * @return number of bytes received on success, -1 otherwise > + */ > +int hip_recvfrom(int fd, void *buf, size_t len, int flags, > + struct sockaddr *addr, socklen_t *addr_len) > +{ > + int err = 0; > + socklen_t socklen = *addr_len; > + struct sockaddr_in6 *peer_hit = (struct sockaddr_in6 *) addr; > + struct in6_addr peer_addr = { { { 0 } } }; > + struct in6_addr *peer_addr6; > + struct in_addr *peer_addr4; > + struct hip_fd_info *fd_info = NULL; > + struct hip_packet_context ctx = { 0 }; > + int (*read_control_msg)(int, struct hip_packet_context *, int) = NULL; > + > + > + if ((fd_info = hip_socket_get_info(fd)) == NULL) { > + HIP_ERROR("Fd %d is not a hip socket, exiting.\n", fd); > + return -1; > + } > + > + /* Bind to a ephemeral port if the src port hasn't been bound yet */ > + if (fd_info->bound_port == 0) { > + if (auto_bind(fd_info)) { > + HIP_ERROR("Fail to bind the hip socket.\n"); > + return -1; > + } > + } > + > + /* Handle BEX if HA hasn't establised */ > + if (!fd_info->ha) { > + if (hip_await_bex(fd_info, addr)) { > + HIP_ERROR("Base exchange not successful.\n"); > + return -1; > + } > + } > + > + ctx.input_msg = hip_msg_alloc(); > + ctx.output_msg = hip_msg_alloc(); > + read_control_msg = fd_info->family == AF_INET ? hip_read_control_msg_v4 > + : hip_read_control_msg_v6; > + > + /* Loop until we get a non-control packet or a CLOSE packet */ > + while (fd_info->ha->state == HIP_STATE_ESTABLISHED) { > + err = recvfrom(fd, buf, len, flags | MSG_PEEK, addr,&socklen); > + HIP_DEBUG("Peek packet len: %d\n", err); > + HIP_DEBUG("peer sockaddr: AF = %d, socklen = %d\n", addr->sa_family, socklen); > + if (err< 0) { > + perror("recvfrom"); > + } > + > + /* Drop the packet if it doesn't come from the address associated > + * with the correct peer. */ > + if (fd_info->proto == IPPROTO_UDP) { > + if (addr->sa_family == AF_INET) { > + peer_addr4 =&((struct sockaddr_in *) addr)->sin_addr; > + IPV4_TO_IPV6_MAP(peer_addr4,&peer_addr); > + peer_addr6 =&peer_addr; > + } else { > + peer_addr6 =&((struct sockaddr_in6 *) addr)->sin6_addr; > + } > + if (ipv6_addr_cmp(&fd_info->ha->peer_addr, peer_addr6)) { > + HIP_DEBUG("Packet not from associated address. Dropping.\n"); > + HIP_DEBUG_IN6ADDR("expected",&fd_info->ha->peer_addr); > + HIP_DEBUG_IN6ADDR("got", peer_addr6); > + err = recvfrom(fd, buf, 1, flags, addr,&socklen); > + HIP_IFEL(err< 0, err, "recvfrom()\n"); > + continue; > + } > + } > + > + /* Receive message */ > + if (hip_is_control_msg(buf, err, fd_info)) { > + HIP_DEBUG("receive a hip control message.\n"); > + if (fd_info->proto == IPPROTO_TCP) { > + if (!hip_read_control_msg_tcp(fd,&ctx)) { > + hip_receive_control_packet(&ctx); > + } > + } else if (!read_control_msg(fd,&ctx, HIP_UDP_ZERO_BYTES_LEN)) { > + hip_receive_control_packet(&ctx); > + } else { > + HIP_ERROR("Error reading control packet\n"); > + } > + err = 0; > + } else { > + HIP_DEBUG("receive a non hip control message.\n"); > + err = recvfrom(fd, buf, len, flags, addr,&socklen); > + HIP_IFEL(err< 0, err, "recvfrom() error\n"); > + break; > + } > + } Unrelated: Could this be a possible DOS, by crafting a packet that triggers one of the nested recvfrom()s but never sends anything after that, causing it to recv forever? If this is the "main socket", it would probably not hang but we could slurp bytes from genuine clients this way.
Never mind: the outer recvfrom() has MSG_PEEK set of course.