/** * Resolves an IP address to a hostname per DNS. * * @param ha The host address to resolve. * @return On success, the hostname is returned. Otherwise, NULL is * returned. The resulting string points to a static buffer. */ const char * host_addr_to_name(host_addr_t addr) { socket_addr_t sa; if (host_addr_can_convert(addr, NET_TYPE_IPV4)) { (void) host_addr_convert(addr, &addr, NET_TYPE_IPV4); } if (0 == socket_addr_set(&sa, addr, 0)) { return NULL; } #ifdef HAS_GETNAMEINFO { static char host[1025]; int error; error = getnameinfo(socket_addr_get_const_sockaddr(&sa), socket_addr_get_len(&sa), host, sizeof host, NULL, 0, 0); if (error) { char buf[HOST_ADDR_BUFLEN]; host_addr_to_string_buf(addr, buf, sizeof buf); g_message("getnameinfo() failed for \"%s\": %s", buf, gai_strerror(error)); return NULL; } return host; } #else /* !HAS_GETNAMEINFO */ { const struct hostent *he; socklen_t len = 0; const char *ptr = NULL; switch (host_addr_net(addr)) { case NET_TYPE_IPV4: ptr = cast_to_gchar_ptr(&sa.inet4.sin_addr); len = sizeof sa.inet4.sin_addr; break; case NET_TYPE_IPV6: #ifdef HAS_IPV6 ptr = cast_to_gchar_ptr(&sa.inet6.sin6_addr); len = sizeof sa.inet6.sin6_addr; break; #endif /* HAS_IPV6 */ case NET_TYPE_LOCAL: case NET_TYPE_NONE: return NULL; } g_return_val_if_fail(ptr, NULL); g_return_val_if_fail(0 != len, NULL); he = gethostbyaddr(ptr, len, socket_addr_get_family(&sa)); if (!he) { char buf[HOST_ADDR_BUFLEN]; host_addr_to_string_buf(addr, buf, sizeof buf); gethostbyname_error(buf); return NULL; } return he->h_name; } #endif /* HAS_GETNAMEINFO */ }
/** * Create a security token from host address and port using specified key. * * Optionally, extra contextual data may be given (i.e. the token is not * only based on the address and port) to make the token more unique to * a specific context. * * @param stg the security token generator * @param n key index to use * @param tok where security token is written * @param addr address of the host for which we're generating a token * @param port port of the host for which we're generating a token * @param data optional contextual data * @param len length of contextual data */ static void sectoken_generate_n(sectoken_gen_t *stg, size_t n, sectoken_t *tok, host_addr_t addr, uint16 port, const void *data, size_t len) { char block[8]; char enc[8]; char *p = block; sectoken_gen_check(stg); g_assert(tok != NULL); g_assert(size_is_non_negative(n)); g_assert(n < stg->keycnt); g_assert((NULL != data) == (len != 0)); switch (host_addr_net(addr)) { case NET_TYPE_IPV4: p = poke_be32(p, host_addr_ipv4(addr)); break; case NET_TYPE_IPV6: { uint val; val = binary_hash(host_addr_ipv6(&addr), 16); p = poke_be32(p, val); } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: g_error("unexpected address for security token generation: %s", host_addr_to_string(addr)); } p = poke_be16(p, port); p = poke_be16(p, 0); /* Filler */ g_assert(p == &block[8]); STATIC_ASSERT(sizeof(tok->v) == sizeof(uint32)); STATIC_ASSERT(sizeof(block) == sizeof(enc)); tea_encrypt(&stg->keys[n], enc, block, sizeof block); /* * If they gave contextual data, encrypt them by block of 8 bytes, * filling the last partial block with zeroes if needed. */ if (data != NULL) { const void *q = data; size_t remain = len; char denc[8]; STATIC_ASSERT(sizeof(denc) == sizeof(enc)); while (remain != 0) { size_t fill = MIN(remain, 8U); unsigned i; if (fill != 8U) ZERO(&block); memcpy(block, q, fill); remain -= fill; q = const_ptr_add_offset(q, fill); /* * Encrypt block of contextual data (possibly filled with trailing * zeroes) and merge back the result into the main encryption * output with XOR. */ tea_encrypt(&stg->keys[n], denc, block, sizeof block); for (i = 0; i < sizeof denc; i++) enc[i] ^= denc[i]; } } poke_be32(tok->v, tea_squeeze(enc, sizeof enc)); }
/** * Initiate an UDP RPC transaction. * * The message held in ``data'' is sent to the specified address and port. * Upon reception of a reply from that host, the callback is invoked. * If no reply is received after some time, the callaback is also invoked. * * @param what type of RPC, for logging (static string) * @param addr address where RPC should be sent to * @param port port where RPC should be sent to * @param data message data to send * @param len length of data to send * @param timeout timeout in milliseconds to get a reply * @param cb callback to invoke on reply or timeout * @param arg additionnal callback argument * * @return 0 if OK, -1 if we could not initiate the RPC, with errno set. */ int urpc_send(const char *what, host_addr_t addr, uint16 port, const void *data, size_t len, unsigned long timeout, urpc_cb_t cb, void *arg) { struct urpc_cb *ucb; struct gnutella_socket *s; host_addr_t bind_addr = zero_host_addr; gnet_host_t to; ssize_t r; /* * Create anonymous socket to send/receive the RPC. */ switch (host_addr_net(addr)) { case NET_TYPE_IPV4: bind_addr = ipv4_unspecified; break; case NET_TYPE_IPV6: bind_addr = ipv6_unspecified; break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: g_assert_not_reached(); } s = socket_udp_listen(bind_addr, 0, urpc_received); if (NULL == s) { if (GNET_PROPERTY(udp_debug)) { g_warning("unable to create anonymous UDP %s socket for %s RPC: %m", net_type_to_string(host_addr_net(bind_addr)), what); } return -1; } /* * Send the message. */ gnet_host_set(&to, addr, port); r = (s->wio.sendto)(&s->wio, &to, data, len); /* * Reset errno if there was no "real" error to prevent getting a * bogus and possibly misleading error message later. */ if ((ssize_t) -1 == r) { if (GNET_PROPERTY(udp_debug)) { g_warning("unable to send UDP %s RPC to %s: %m", what, host_addr_port_to_string(addr, port)); } } else { errno = 0; } if (len != UNSIGNED(r)) { if ((ssize_t) -1 != r) { if (GNET_PROPERTY(udp_debug)) { g_warning("unable to send whole %zu-byte UDP %s RPC to %s: " "only sent %zu byte%s", len, what, host_addr_port_to_string(addr, port), r, 1 == r ? "" : "s"); } } socket_free_null(&s); errno = EIO; return -1; } /* * Make sure socket_udp_event() will only process replies one at a time * since we're going to close the anonymous UDP socket as soon as we * get a reply. */ socket_set_single(s, TRUE); /* * Message was sent, wait for the answer. */ WALLOC(ucb); ucb->magic = URPC_CB_MAGIC; ucb->addr = addr; ucb->port = port; ucb->s = s; ucb->cb = cb; ucb->arg = arg; ucb->timeout_ev = cq_main_insert(timeout, urpc_timed_out, ucb); ucb->what = what; htable_insert(pending, s, ucb); return 0; }