comparison libnnchat.c @ 352:b54c8545dcb0

Overhaul network code a bit, add initial implementation of SOCKS4/4A proxy support -- which may not work yet, it is untested.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 23 Jun 2011 06:28:40 +0300
parents 8e509d6546d3
children c01e42fc9adb
comparison
equal deleted inserted replaced
351:b18b97d3e4f5 352:b54c8545dcb0
51 #endif 51 #endif
52 52
53 53
54 void nn_conn_err(nn_conn_t *conn, const char *fmt, ...) 54 void nn_conn_err(nn_conn_t *conn, const char *fmt, ...)
55 { 55 {
56 conn->err = TRUE;
57
58 if (conn->errfunc) { 56 if (conn->errfunc) {
59 va_list ap; 57 va_list ap;
60 va_start(ap, fmt); 58 va_start(ap, fmt);
61 conn->errfunc(conn, fmt, ap); 59 conn->errfunc(conn, fmt, ap);
62 va_end(ap); 60 va_end(ap);
72 conn->msgfunc(conn, fmt, ap); 70 conn->msgfunc(conn, fmt, ap);
73 va_end(ap); 71 va_end(ap);
74 } 72 }
75 } 73 }
76 74
77 75 static const char *nn_proxy_types[] = {
78 nn_conn_t *nn_conn_open(struct in_addr *addr, const int port) 76 "none",
77 "SOCKS 4",
78 "SOCKS 4a",
79 NULL
80 };
81
82 nn_conn_t * nn_conn_open(
83 void (*errfunc)(struct _nn_conn_t *conn, const char *fmt, va_list ap),
84 void (*msgfunc)(struct _nn_conn_t *conn, const char *fmt, va_list ap),
85 int ptype, int pport, struct in_addr *paddr,
86 struct in_addr *addr, const int port, const char *host)
79 { 87 {
80 nn_conn_t *conn = th_calloc(1, sizeof(nn_conn_t)); 88 nn_conn_t *conn = th_calloc(1, sizeof(nn_conn_t));
81 struct sockaddr_in dest; 89 struct sockaddr_in dest;
82 90 static const char *userid = "James Bond";
91
83 if (conn == NULL) 92 if (conn == NULL)
84 return NULL; 93 return NULL;
85 94
86 dest.sin_family = AF_INET; 95 /* Initialize data */
87 dest.sin_port = htons(port); 96 conn->errfunc = errfunc;
88 dest.sin_addr = *addr; 97 conn->msgfunc = msgfunc;
89 98
90 nn_conn_msg(conn, "Connecting to %s:%d ...\n", 99 conn->proxy.type = ptype;
91 inet_ntoa(dest.sin_addr), port); 100 conn->proxy.port = pport;
101 if (paddr != NULL)
102 conn->proxy.addr = *paddr;
103
104 conn->port = port;
105 conn->addr = *addr;
106
107 if (ptype == NN_PROXY_SOCKS4A && host == NULL) {
108 nn_conn_err(conn, "Host string NULL and proxy type SOCKS 4a specified. Using IP address instead.\n");
109 conn->host = th_strdup(inet_ntoa(dest.sin_addr));
110 } else
111 conn->host = th_strdup(host);
112
113
114 /* Prepare for connection */
115 if (ptype > NN_PROXY_NONE && ptype < NN_PROXY_LAST) {
116 dest.sin_family = AF_INET;
117 dest.sin_port = htons(pport);
118 dest.sin_addr = conn->proxy.addr;
119
120 nn_conn_msg(conn, "Connecting to %s proxy %s:%d ...\n",
121 nn_proxy_types[ptype],
122 inet_ntoa(dest.sin_addr), port);
123 } else {
124 dest.sin_family = AF_INET;
125 dest.sin_port = htons(port);
126 dest.sin_addr = *addr;
127
128 nn_conn_msg(conn, "Connecting to %s:%d ...\n",
129 inet_ntoa(dest.sin_addr), port);
130 }
92 131
93 if ((conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) { 132 if ((conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
94 nn_conn_err(conn, "Could not open socket: %s\n", strerror(errno)); 133 conn->err = nn_get_socket_errno();
95 conn->status = NN_CONN_CLOSED; 134 nn_conn_err(conn, "Could not open socket: %s\n", nn_get_socket_errstr(conn->err));
96 return conn; 135 goto error;
97 } 136 }
98 137
99 nn_conn_msg(conn, "Using socket %d.\n", conn->socket); 138 nn_conn_msg(conn, "Using socket %d.\n", conn->socket);
100 139
101 if (connect(conn->socket, (struct sockaddr *) &dest, sizeof(dest)) == -1) { 140 if (connect(conn->socket, (struct sockaddr *) &dest, sizeof(dest)) == -1) {
102 nn_conn_err(conn, "Could not connect: %s\n", strerror(errno)); 141 conn->err = nn_get_socket_errno();
103 conn->status = NN_CONN_CLOSED; 142 nn_conn_err(conn, "Could not connect: %s\n", nn_get_socket_errstr(conn->err));
104 return conn; 143 goto error;
105 } 144 }
106 145
107 conn->port = port;
108 conn->address = *addr;
109 FD_ZERO(&(conn->sockfds)); 146 FD_ZERO(&(conn->sockfds));
110 FD_SET(conn->socket, &(conn->sockfds)); 147 FD_SET(conn->socket, &(conn->sockfds));
148
149 /* Proxy-specific setup */
150 if (ptype == NN_PROXY_SOCKS4 || ptype == NN_PROXY_SOCKS4A) {
151 struct nn_socks_t *socksh;
152 size_t bufsiz = sizeof(struct nn_socks_t) + strlen(userid) + 1 + strlen(conn->host) + 1;
153 char *ptr, *buf;
154 int tries, status = -1;
155
156 ptr = buf = th_malloc(bufsiz);
157 if (buf == NULL) {
158 conn->err = -1;
159 nn_conn_err(conn, "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz);
160 goto error;
161 }
162
163 /* Create SOCKS 4/4A request */
164 nn_conn_msg(conn, "Initializing proxy negotiation.\n");
165 socksh = (struct nn_socks_t *) buf;
166 socksh->version = (ptype == NN_PROXY_SOCKS4) ? 4 : 5;
167 socksh->command = SOCKS_CMD_CONNECT;
168 socksh->port = htons(port);
169 if (ptype == NN_PROXY_SOCKS4A)
170 socksh->addr = htonl(0x00000032);
171 else
172 socksh->addr = dest.sin_addr.s_addr;
173 ptr += sizeof(struct nn_socks_t);
174
175 strcpy(ptr, userid);
176 ptr += strlen(userid) + 1;
177
178 strcpy(ptr, conn->host);
179
180 /* Send request */
181 if (!nn_conn_send_buf(conn, buf, bufsiz)) {
182 th_free(buf);
183 nn_conn_err(conn, "Error sending SOCKS proxy request.\n");
184 goto error;
185 }
186 th_free(buf);
187
188 /* Wait for SOCKS server to reply */
189 for (tries = 0; tries < 20; tries++) {
190 status = nn_conn_pull(conn);
191 if (status <= 0) break;
192 }
193
194 /* Check results */
195 if (status == 0) {
196 struct nn_socks_res_t *res = (struct nn_socks_res_t *) &(conn->buf);
197 if (res->nb != 0) {
198 nn_conn_err(conn, "Invalid SOCKS server reply, does not begin with NUL byte (%d).\n", res->nb);
199 goto error;
200 }
201 if (res->result != 0x5a) {
202 char *s = NULL;
203 switch (res->result) {
204 case 0x5b: s = "Request rejected or failed"; break;
205 case 0x5c: s = "Request failed because client is not running identd (or not reachable from the server)"; break;
206 case 0x5d: s = "Request failed because client's identd could not confirm the user ID string in the request"; break;
207 default: s = "Unknown SOCKS error response"; break;
208 }
209 nn_conn_err(conn, "SOCKS setup failed, 0x%02x: %s.\n", res->result, s);
210 goto error;
211 }
212 }
213 else
214 goto error;
215 }
216
111 conn->status = NN_CONN_OPEN; 217 conn->status = NN_CONN_OPEN;
112 218 return conn;
219
220 error:
221 conn->status = NN_CONN_CLOSED;
113 return conn; 222 return conn;
114 } 223 }
115 224
116 225
117 void nn_conn_close(nn_conn_t *conn) 226 void nn_conn_close(nn_conn_t *conn)
141 250
142 while (bufLeft > 0) { 251 while (bufLeft > 0) {
143 ssize_t bufSent; 252 ssize_t bufSent;
144 bufSent = send(conn->socket, bufPtr, bufLeft, 0); 253 bufSent = send(conn->socket, bufPtr, bufLeft, 0);
145 if (bufSent < 0) { 254 if (bufSent < 0) {
146 nn_conn_err(conn, "nn_conn_send_buf() failed: %s", strerror(errno)); 255 conn->err = nn_get_socket_errno();
256 nn_conn_err(conn, "nn_conn_send_buf() failed: %s", nn_get_socket_errstr(conn->err));
147 return FALSE; 257 return FALSE;
148 } 258 }
149 bufLeft -= bufSent; 259 bufLeft -= bufSent;
150 bufPtr += bufSent; 260 bufPtr += bufSent;
151 } 261 }
152 262
153 return TRUE; 263 return TRUE;
154 } 264 }
155 265
156 266
157 BOOL nn_conn_pull(nn_conn_t *conn) 267 int nn_conn_pull(nn_conn_t *conn)
158 { 268 {
159 int result; 269 int result;
160 struct timeval socktv; 270 struct timeval socktv;
161 fd_set tmpfds; 271 fd_set tmpfds;
162 272
169 socktv.tv_sec = 0; 279 socktv.tv_sec = 0;
170 socktv.tv_usec = NN_DELAY_USEC; 280 socktv.tv_usec = NN_DELAY_USEC;
171 tmpfds = conn->sockfds; 281 tmpfds = conn->sockfds;
172 282
173 if ((result = select(conn->socket + 1, &tmpfds, NULL, NULL, &socktv)) == -1) { 283 if ((result = select(conn->socket + 1, &tmpfds, NULL, NULL, &socktv)) == -1) {
174 int err = nn_get_socket_errno(); 284 conn->err = nn_get_socket_errno();
175 if (err != EINTR) { 285 if (conn->err != EINTR) {
176 nn_conn_err(conn, "Error occured in select(%d, sockfds): %d, %s\n", 286 nn_conn_err(conn, "Error occured in select(%d, sockfds): %d, %s\n",
177 socket, err, nn_get_socket_errstr(err)); 287 socket, conn->err, nn_get_socket_errstr(conn->err));
178 return -1; 288 return -1;
179 } 289 }
180 } else 290 } else
181 if (FD_ISSET(conn->socket, &tmpfds)) { 291 if (FD_ISSET(conn->socket, &tmpfds)) {
182 conn->got = recv(conn->socket, conn->ptr, NN_CONNBUF_SIZE, 0); 292 conn->got = recv(conn->socket, conn->ptr, NN_CONNBUF_SIZE, 0);
183
184 if (conn->got < 0) { 293 if (conn->got < 0) {
185 int res = nn_get_socket_errno(); 294 conn->err = nn_get_socket_errno();
186 nn_conn_err(conn, "Error in recv: %d, %s\n", res, nn_get_socket_errstr(res)); 295 nn_conn_err(conn, "Error in recv: %d, %s\n", conn->err, nn_get_socket_errstr(conn->err));
187 return -2; 296 return -2;
188 } else if (conn->got == 0) { 297 } else if (conn->got == 0) {
189 nn_conn_err(conn, "Server closed connection.\n"); 298 nn_conn_err(conn, "Server closed connection.\n");
190 conn->status = NN_CONN_CLOSED; 299 conn->status = NN_CONN_CLOSED;
191 return -3; 300 return -3;
931 void nn_strtuple_free(nn_strtuple_t *tuple) 1040 void nn_strtuple_free(nn_strtuple_t *tuple)
932 { 1041 {
933 th_free(tuple->str); 1042 th_free(tuple->str);
934 th_free(tuple); 1043 th_free(tuple);
935 } 1044 }
936
937