Eric Andersen | 0b31586 | 2002-07-03 11:51:44 +0000 | [diff] [blame] | 1 | /* vi: set sw=4 ts=4: */ |
| 2 | /* |
| 3 | * Utility routines. |
| 4 | * |
Eric Andersen | aff114c | 2004-04-14 17:51:38 +0000 | [diff] [blame] | 5 | * Connect to host at port using address resolution from getaddrinfo |
Eric Andersen | 0b31586 | 2002-07-03 11:51:44 +0000 | [diff] [blame] | 6 | * |
| 7 | */ |
| 8 | |
Eric Andersen | 0b31586 | 2002-07-03 11:51:44 +0000 | [diff] [blame] | 9 | #include "libbb.h" |
| 10 | |
Glenn L McGrath | ffccf6e | 2003-12-20 01:47:18 +0000 | [diff] [blame] | 11 | /* Return network byte ordered port number for a service. |
| 12 | * If "port" is a number use it as the port. |
| 13 | * If "port" is a name it is looked up in /etc/services, if it isnt found return |
| 14 | * default_port |
| 15 | */ |
Glenn L McGrath | 036dbaa | 2004-01-17 05:03:31 +0000 | [diff] [blame] | 16 | unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port) |
Eric Andersen | 0b31586 | 2002-07-03 11:51:44 +0000 | [diff] [blame] | 17 | { |
Glenn L McGrath | ffccf6e | 2003-12-20 01:47:18 +0000 | [diff] [blame] | 18 | unsigned short port_nr = htons(default_port); |
| 19 | if (port) { |
Eric Andersen | 9cccc18 | 2003-12-23 20:37:23 +0000 | [diff] [blame] | 20 | char *endptr; |
| 21 | int old_errno; |
| 22 | long port_long; |
Eric Andersen | 0b31586 | 2002-07-03 11:51:44 +0000 | [diff] [blame] | 23 | |
Eric Andersen | 9cccc18 | 2003-12-23 20:37:23 +0000 | [diff] [blame] | 24 | /* Since this is a lib function, we're not allowed to reset errno to 0. |
| 25 | * Doing so could break an app that is deferring checking of errno. */ |
| 26 | old_errno = errno; |
| 27 | errno = 0; |
| 28 | port_long = strtol(port, &endptr, 10); |
Glenn L McGrath | ffccf6e | 2003-12-20 01:47:18 +0000 | [diff] [blame] | 29 | if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) { |
Glenn L McGrath | 036dbaa | 2004-01-17 05:03:31 +0000 | [diff] [blame] | 30 | struct servent *tserv = getservbyname(port, protocol); |
Glenn L McGrath | ffccf6e | 2003-12-20 01:47:18 +0000 | [diff] [blame] | 31 | if (tserv) { |
Eric Andersen | 3a5ed56 | 2003-12-23 20:37:54 +0000 | [diff] [blame] | 32 | port_nr = tserv->s_port; |
| 33 | } |
| 34 | } else { |
Glenn L McGrath | ffccf6e | 2003-12-20 01:47:18 +0000 | [diff] [blame] | 35 | port_nr = htons(port_long); |
| 36 | } |
Eric Andersen | 9cccc18 | 2003-12-23 20:37:23 +0000 | [diff] [blame] | 37 | errno = old_errno; |
Eric Andersen | e6dc439 | 2003-10-31 09:31:46 +0000 | [diff] [blame] | 38 | } |
| 39 | return port_nr; |
| 40 | } |
| 41 | |
Glenn L McGrath | ffccf6e | 2003-12-20 01:47:18 +0000 | [diff] [blame] | 42 | void bb_lookup_host(struct sockaddr_in *s_in, const char *host) |
Eric Andersen | e6dc439 | 2003-10-31 09:31:46 +0000 | [diff] [blame] | 43 | { |
| 44 | struct hostent *he; |
| 45 | |
| 46 | memset(s_in, 0, sizeof(struct sockaddr_in)); |
| 47 | s_in->sin_family = AF_INET; |
| 48 | he = xgethostbyname(host); |
| 49 | memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length); |
Eric Andersen | e6dc439 | 2003-10-31 09:31:46 +0000 | [diff] [blame] | 50 | } |
| 51 | |
Denis Vlasenko | 1457915 | 2006-10-26 01:09:46 +0000 | [diff] [blame] | 52 | void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) |
| 53 | { |
| 54 | if (connect(s, s_addr, addrlen) < 0) { |
| 55 | if (ENABLE_FEATURE_CLEAN_UP) close(s); |
| 56 | if (s_addr->sa_family == AF_INET) |
Denis Vlasenko | 89f0b34 | 2006-11-18 22:04:09 +0000 | [diff] [blame] | 57 | bb_perror_msg_and_die("cannot connect to remote host (%s)", |
Denis Vlasenko | 1457915 | 2006-10-26 01:09:46 +0000 | [diff] [blame] | 58 | inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); |
Denis Vlasenko | 89f0b34 | 2006-11-18 22:04:09 +0000 | [diff] [blame] | 59 | bb_perror_msg_and_die("cannot connect to remote host"); |
Denis Vlasenko | 1457915 | 2006-10-26 01:09:46 +0000 | [diff] [blame] | 60 | } |
| 61 | } |
| 62 | |
| 63 | int xconnect_tcp_v4(struct sockaddr_in *s_addr) |
Eric Andersen | e6dc439 | 2003-10-31 09:31:46 +0000 | [diff] [blame] | 64 | { |
Rob Landley | d921b2e | 2006-08-03 15:41:12 +0000 | [diff] [blame] | 65 | int s = xsocket(AF_INET, SOCK_STREAM, 0); |
Denis Vlasenko | 1457915 | 2006-10-26 01:09:46 +0000 | [diff] [blame] | 66 | xconnect(s, (struct sockaddr*) s_addr, sizeof(*s_addr)); |
Eric Andersen | 0b31586 | 2002-07-03 11:51:44 +0000 | [diff] [blame] | 67 | return s; |
Eric Andersen | 0b31586 | 2002-07-03 11:51:44 +0000 | [diff] [blame] | 68 | } |
Denis Vlasenko | c8717cd | 2006-11-22 16:10:39 +0000 | [diff] [blame] | 69 | |
Denis Vlasenko | 48237b0 | 2006-11-22 23:22:06 +0000 | [diff] [blame] | 70 | static const int one = 1; |
| 71 | int setsockopt_reuseaddr(int fd) |
| 72 | { |
| 73 | return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); |
| 74 | } |
| 75 | int setsockopt_broadcast(int fd) |
| 76 | { |
| 77 | return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)); |
| 78 | } |
| 79 | |
Denis Vlasenko | c8717cd | 2006-11-22 16:10:39 +0000 | [diff] [blame] | 80 | int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen) |
| 81 | { |
| 82 | union { |
| 83 | struct in_addr a4; |
| 84 | #if ENABLE_FEATURE_IPV6 |
| 85 | struct in6_addr a6; |
| 86 | #endif |
| 87 | } a; |
| 88 | |
| 89 | /* TODO maybe: port spec? like n.n.n.n:nn */ |
| 90 | |
| 91 | #if ENABLE_FEATURE_IPV6 |
| 92 | if (socklen >= sizeof(struct sockaddr_in6) |
| 93 | && inet_pton(AF_INET6, dotted, &a.a6) > 0 |
| 94 | ) { |
| 95 | ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6; |
| 96 | ((struct sockaddr_in6*)sp)->sin6_addr = a.a6; |
| 97 | /* ((struct sockaddr_in6*)sp)->sin6_port = */ |
| 98 | return 0; /* success */ |
| 99 | } |
| 100 | #endif |
| 101 | if (socklen >= sizeof(struct sockaddr_in) |
| 102 | && inet_pton(AF_INET, dotted, &a.a4) > 0 |
| 103 | ) { |
| 104 | ((struct sockaddr_in*)sp)->sin_family = AF_INET; |
| 105 | ((struct sockaddr_in*)sp)->sin_addr = a.a4; |
| 106 | /* ((struct sockaddr_in*)sp)->sin_port = */ |
| 107 | return 0; /* success */ |
| 108 | } |
| 109 | return 1; |
| 110 | } |
| 111 | |
| 112 | int xsocket_stream_ip4or6(sa_family_t *fp) |
| 113 | { |
| 114 | int fd; |
| 115 | #if ENABLE_FEATURE_IPV6 |
| 116 | fd = socket(AF_INET6, SOCK_STREAM, 0); |
| 117 | if (fp) *fp = AF_INET6; |
| 118 | if (fd < 0) |
| 119 | #endif |
| 120 | { |
| 121 | fd = xsocket(AF_INET, SOCK_STREAM, 0); |
| 122 | if (fp) *fp = AF_INET; |
| 123 | } |
| 124 | return fd; |
| 125 | } |
| 126 | |
| 127 | int create_and_bind_socket_ip4or6(const char *hostaddr, int port) |
| 128 | { |
Denis Vlasenko | c8717cd | 2006-11-22 16:10:39 +0000 | [diff] [blame] | 129 | int fd; |
| 130 | sockaddr_inet sa; |
| 131 | |
| 132 | memset(&sa, 0, sizeof(sa)); |
Denis Vlasenko | c8717cd | 2006-11-22 16:10:39 +0000 | [diff] [blame] | 133 | if (hostaddr) { |
| 134 | if (dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) |
| 135 | bb_error_msg_and_die("bad address '%s'", hostaddr); |
| 136 | /* user specified bind addr dictates family */ |
| 137 | fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0); |
| 138 | } else |
| 139 | fd = xsocket_stream_ip4or6(&sa.sa.sa_family); |
Denis Vlasenko | 48237b0 | 2006-11-22 23:22:06 +0000 | [diff] [blame] | 140 | setsockopt_reuseaddr(fd); |
Denis Vlasenko | c8717cd | 2006-11-22 16:10:39 +0000 | [diff] [blame] | 141 | |
| 142 | /* if (port >= 0) { */ |
| 143 | #if ENABLE_FEATURE_IPV6 |
| 144 | if (sa.sa.sa_family == AF_INET6 /* && !sa.sin6.sin6_port */) |
| 145 | sa.sin6.sin6_port = htons(port); |
| 146 | #endif |
| 147 | if (sa.sa.sa_family == AF_INET /* && !sa.sin.sin_port */) |
| 148 | sa.sin.sin_port = htons(port); |
| 149 | /* } */ |
| 150 | |
| 151 | xbind(fd, &sa.sa, sizeof(sa)); |
| 152 | return fd; |
| 153 | } |