/* * Torsocks call for connect(2). */ LIBC_CONNECT_RET_TYPE tsocks_connect(LIBC_CONNECT_SIG) { int ret, ret_errno; struct connection *new_conn; struct onion_entry *on_entry; DBG("Connect caught on fd %d", sockfd); /* * Validate socket values in order to see if we can handle this connect * through Tor. */ ret = tsocks_validate_socket(sockfd, addr); if (ret == 1) { /* Tor can't handle it so send it to the libc. */ goto libc_connect; } else if (ret == -1) { /* Validation failed. Stop right now. */ goto error; } /* Implicit else statement meaning we continue processing the connect. */ assert(!ret); /* * Lock registry to get the connection reference if one. In this code path, * if a connection object is found, it will not be used since a double * connect() on the same file descriptor is an error so the registry is * quickly unlocked and no reference is needed. */ connection_registry_lock(); new_conn = connection_find(sockfd); connection_registry_unlock(); if (new_conn) { /* Double connect() for the same fd. */ errno = EISCONN; goto error; } /* * See if the IP being connected is an onion IP cookie mapping to an * existing .onion address. */ onion_pool_lock(&tsocks_onion_pool); on_entry = onion_entry_find_by_addr(addr, &tsocks_onion_pool); onion_pool_unlock(&tsocks_onion_pool); if (on_entry) { /* * Create a connection with the onion IP cookie since getpeername() * might need it, and set connection domain and hostname to use * the onion address name found before. */ new_conn = connection_create(sockfd, addr); if (!new_conn) { errno = ENOMEM; goto error; } new_conn->dest_addr.domain = CONNECTION_DOMAIN_NAME; new_conn->dest_addr.hostname.port = utils_get_port_from_addr(addr); new_conn->dest_addr.hostname.addr = strdup(on_entry->hostname); if (!new_conn->dest_addr.hostname.addr) { ret_errno = ENOMEM; goto error_free; } } else { /* * Check if address is localhost. At this point, we are sure it's not a * .onion cookie address that is by default in the loopback network * thus this check is done after the onion entry lookup. */ if (utils_sockaddr_is_localhost(addr)) { /* * Certain setups need to be able to reach localhost, despite * running torsocks. If they enabled the config option, allow such * connections. */ if (tsocks_config.allow_outbound_localhost) { goto libc_connect; } WARN("[connect] Connection to a local address are denied since it " "might be a TCP DNS query to a local DNS server. " "Rejecting it for safety reasons."); errno = EPERM; goto error; } new_conn = connection_create(sockfd, addr); if (!new_conn) { errno = ENOMEM; goto error; } } /* Connect the socket to the Tor network. */ ret = tsocks_connect_to_tor(new_conn); if (ret < 0) { ret_errno = -ret; goto error_free; } connection_registry_lock(); /* This can't fail since a lookup was done previously. */ connection_insert(new_conn); connection_registry_unlock(); /* Flag errno for success */ ret = errno = 0; return ret; libc_connect: return tsocks_libc_connect(LIBC_CONNECT_ARGS); error_free: /* * Put back reference of newly created connection. Will be freed if * refcount goes down to 0. */ connection_put_ref(new_conn); errno = ret_errno; error: /* At this point, errno MUST be set to a valid connect() error value. */ return -1; }
/* * Torsocks call for connect(2). */ LIBC_CONNECT_RET_TYPE tsocks_connect(LIBC_CONNECT_SIG) { int ret, sock_type; socklen_t optlen; struct connection *new_conn; struct onion_entry *on_entry; struct sockaddr_in *inet_addr; DBG("Connect catched on fd %d", __sockfd); optlen = sizeof(sock_type); ret = getsockopt(__sockfd, SOL_SOCKET, SO_TYPE, &sock_type, &optlen); if (ret < 0) { /* Use the getsockopt() errno value. */ goto error; } /* We can't handle a non inet socket. */ if (__addr->sa_family != AF_INET && __addr->sa_family != AF_INET6) { DBG("[conect] Connection is not IPv4/v6. Ignoring."); goto libc_connect; } /* * Refuse non stream socket. There is a chance that this might be a DNS * request that we can't pass through Tor using raw UDP packet. */ if (sock_type != SOCK_STREAM) { WARN("[connect] UDP or ICMP stream can't be handled. Rejecting."); errno = EBADF; goto error; } DBG("[connect] Socket family %s and type %d", __addr->sa_family == AF_INET ? "AF_INET" : "AF_INET6", sock_type); inet_addr = (struct sockaddr_in *) __addr; /* * Lock registry to get the connection reference if one. In this code path, * if a connection object is found, it will not be used since a double * connect() on the same file descriptor is an error so the registry is * quickly unlocked and no reference is needed. */ connection_registry_lock(); new_conn = connection_find(__sockfd); connection_registry_unlock(); if (new_conn) { /* Double connect() for the same fd. */ errno = EISCONN; goto error; } /* * See if the IP being connected is an onion IP cookie mapping to an * existing .onion address. */ onion_pool_lock(&tsocks_onion_pool); on_entry = onion_entry_find_by_ip(inet_addr->sin_addr.s_addr, &tsocks_onion_pool); onion_pool_unlock(&tsocks_onion_pool); if (on_entry) { /* * Create a connection without a destination address since we will set * the onion address name found before. */ new_conn = connection_create(__sockfd, NULL); if (!new_conn) { errno = ENOMEM; goto error; } new_conn->dest_addr.domain = CONNECTION_DOMAIN_NAME; new_conn->dest_addr.hostname.addr = strdup(on_entry->hostname); new_conn->dest_addr.hostname.port = inet_addr->sin_port; } else { /* * Check if address is local IPv4. At this point, we are sure it's not * a .onion cookie address that is by default in the loopback network. */ if (__addr->sa_family == AF_INET && utils_is_ipv4_local(inet_addr->sin_addr.s_addr)) { WARN("[connect] Connection to a local address are denied since it " "might be a TCP DNS query to a local DNS server. " "Rejecting it for safety reasons."); errno = EPERM; goto error; } new_conn = connection_create(__sockfd, __addr); if (!new_conn) { errno = ENOMEM; goto error; } } /* Connect the socket to the Tor network. */ ret = tsocks_connect_to_tor(new_conn); if (ret < 0) { errno = -ret; goto error; } connection_registry_lock(); /* This can't fail since a lookup was done previously. */ connection_insert(new_conn); connection_registry_unlock(); /* Flag errno for success */ ret = errno = 0; return ret; libc_connect: return tsocks_libc_connect(LIBC_CONNECT_ARGS); error: /* At this point, errno MUST be set to a valid connect() error value. */ return -1; }