Mercurial > hg > nnchat
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 |