]> jfr.im git - irc/quakenet/newserv.git/blame - nterface/nterfaced_service_link.c
Merged in nterface NU.
[irc/quakenet/newserv.git] / nterface / nterfaced_service_link.c
CommitLineData
18f8bd28
CP
1/*
2 nterfaced service TCP link module
3 Copyright (C) 2003-2004 Chris Porter.
4*/
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <stdarg.h>
9#include <netdb.h>
10#include <errno.h>
11#include <sys/ioctl.h>
12#include <sys/types.h>
13#include <netinet/in.h>
14#include <sys/socket.h>
15#include <unistd.h>
16#include <string.h>
17
18#include "../core/config.h"
19#include "../core/schedule.h"
20#include "../core/events.h"
21#include "../lib/sstring.h"
22
23#include "nterfaced_service_link.h"
24#include "logging.h"
25
26struct transport *otsl;
27struct slink *slinks;
28int link_count = 0;
29
30struct esocket_events sl_events;
31
32void _init(void) {
33 otsl = register_transport("service_link");
34 MemCheck(otsl);
35
36 otsl->on_line = s_l_transport_line_event;
37 otsl->on_disconnect = s_l_transport_disconnect_event;
38 otsl->type = TT_OUTPUT;
39
40 sl_events.on_connect = s_l_buffer_connect_event;
41 sl_events.on_disconnect = s_l_buffer_disconnect_event;
42 sl_events.on_line = s_l_buffer_line_event;
43
44 scheduleoneshot(time(NULL) + 1, service_link_startup, NULL);
45}
46
47void _fini(void) {
48 int i;
49
50 if(otsl) {
51 deregister_transport(otsl);
52 otsl = NULL;
53 }
54
55 if(slinks) {
56 for(i=0;i<link_count;i++) {
57 if(slinks[i].status != SL_IDLE)
58 esocket_disconnect(slinks[i].socket);
59
60 if(slinks[i].reconnect)
61 deleteschedule(slinks[i].reconnect, &connect_link, &slinks[i]);
62
63 freesstring(slinks[i].hostname);
64 freesstring(slinks[i].password);
65 }
66
67 free(slinks);
68 slinks = NULL;
69 link_count = 0;
70 }
71}
72
73void service_link_startup(void *args) {
74 int loaded = load_links(), i;
75 if(!loaded) {
76 nterface_log(ndl, NL_ERROR, "SL: No config lines loaded successfully.");
77 return;
78 } else {
79 nterface_log(ndl, NL_INFO, "SL: Loaded %d link%s successfully.", loaded, loaded==1?"":"s");
80 }
81
82 for(i=0;i<loaded;i++)
83 connect_link(&slinks[i]);
84}
85
86int load_links(void) {
87 int lines, i, loaded_lines = 0;
88 struct slink *new_links, *resized, *item, *ci;
89 char buf[50];
90
91 lines = getcopyconfigitemintpositive("nterfaced", "links", 0);
92 if(lines < 1) {
93 nterface_log(ndl, NL_ERROR, "SL: No links found in config file.");
94 return 0;
95 }
96 nterface_log(ndl, NL_INFO, "SL: Loading %d link%s from config file", lines, lines==1?"":"s");
97
98 new_links = calloc(lines, sizeof(struct slink));
99 item = new_links;
100
101 for(i=1;i<=lines;i++) {
102 snprintf(buf, sizeof(buf), "port%d", i);
103 item->port = getcopyconfigitemintpositive("nterfaced", buf, 0);
104 if(item->port < 1) {
105 nterface_log(ndl, NL_WARNING, "SL: Error loading port for item %d.", i);
106 continue;
107 }
108
109 snprintf(buf, sizeof(buf), "hostname%d", i);
110 item->hostname = getcopyconfigitem("nterfaced", buf, "", 100);
111 if(!item->hostname) {
112 MemError();
113 continue;
114 }
115
116 for(ci=new_links;ci<item;ci++) {
117 if((ci->port == item->port) && !strcmp(ci->hostname->content, item->hostname->content)) {
118 nterface_log(ndl, NL_WARNING, "SL: Duplicate hostname/port %s:%d (%d:%d), ignoring item at position %d.", item->hostname->content, item->port, ci - new_links + 1, i, i);
119 item->port = 0;
120 break;
121 }
122 }
123
124 if(!item->port) {
125 freesstring(item->hostname);
126 continue;
127 }
128
129 snprintf(buf, sizeof(buf), "password%d", i);
130 item->password = getcopyconfigitem("nterfaced", buf, "", 100);
131 if(!item->password) {
132 MemError();
133 freesstring(item->hostname);
134 continue;
135 }
136
137 nterface_log(ndl, NL_DEBUG, "SL: Loaded link, hostname: %s port: %d.", item->hostname->content, item->port);
138
139 item->status = SL_IDLE;
140 item->reconnect = NULL;
141 item->tagid = i;
142
143 item++;
144 loaded_lines++;
145 }
146
147 if(!loaded_lines) {
148 free(new_links);
149 return 0;
150 }
151
152 resized = realloc(new_links, sizeof(struct slink) * loaded_lines);
153 if(!resized) {
154 MemError();
155 free(new_links);
156 return 0;
157 }
158 slinks = resized;
159 link_count = loaded_lines;
160
161 return link_count;
162}
163
164void connect_link(void *vlink) {
165 struct slink *nlink = (struct slink *)vlink;
166 struct sockaddr_in sin;
167 struct hostent *host;
168 unsigned int opt = 1;
169 int res = 1;
170 int fd;
171
172 if(nlink->reconnect)
173 nlink->reconnect = NULL;
174
175 if(nlink->status != SL_IDLE)
176 return;
177
178 nterface_log(ndl, NL_INFO, "SL: Connecting to %s:%d. . .", nlink->hostname->content, nlink->port);
179
180 /* shamelessly ripped from irc/irc.c and proxyscan/proxyscanconnect.c */
181
182 host = gethostbyname(nlink->hostname->content);
183
184 if (!host) {
185 nterface_log(ndl, NL_WARNING, "SL: Couldn't resolve hostname: %s (port %d) retrying in %d seconds.", nlink->hostname->content, nlink->port, DNSERROR_DURATION);
186 nlink->reconnect = scheduleoneshot(time(NULL) + DNSERROR_DURATION, &connect_link, nlink);
187 return;
188 }
189
190 fd = socket(AF_INET, SOCK_STREAM, 0);
191
192 if (fd < 0) {
193 nterface_log(ndl, NL_WARNING, "SL: Couldn't create socket for %s:%d, retrying in %d seconds.", nlink->hostname->content, nlink->port, RECONNECT_DURATION);
194 link_schedule_reconnect(nlink);
195 return;
196 }
197
198 memset(&sin, 0, sizeof(sin));
199
200 sin.sin_family = AF_INET;
201 sin.sin_port = htons(nlink->port);
202
203 memcpy(&sin.sin_addr.s_addr, host->h_addr, host->h_length);
204
205 if (ioctl(fd, FIONBIO, &res)) {
206 nterface_log(ndl, NL_ERROR, "SL: Unable to make socket nonblocking (%s:%d), retrying in %d seconds.", nlink->hostname->content, nlink->port, RECONNECT_DURATION);
207 close(fd);
208 link_schedule_reconnect(nlink);
209 return;
210 }
211
212 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt))) {
213 nterface_log(ndl, NL_ERROR, "SL: Unable to set SO_REUSEADDR on socket (%s:%d), retrying in %d seconds.", nlink->hostname->content, nlink->port, RECONNECT_DURATION);
214 close(fd);
215 link_schedule_reconnect(nlink);
216 return;
217 }
218
219#ifdef __FreeBSD__
220 opt = IP_PORTRANGE_HIGH;
221 if (setsockopt(fd, IPPROTO_IP, IP_PORTRANGE, (char *)&opt, sizeof(opt)))
222 nterface_log(ndl, NL_WARNING, "SL: Error selecting high port range (%s:%d).", nlink->hostname->content, nlink->port);
223#endif
224
225 if(!connect(fd, (const struct sockaddr *)&sin, sizeof(sin))) {
226 nterface_log(ndl, NL_INFO, "SL: Connected to %s:%d", nlink->hostname->content, nlink->port);
227 nlink->socket = esocket_add(fd, ESOCKET_OUTGOING_TCP_CONNECTED, &sl_events, BLANK_TOKEN);
228
229 if(!nlink->socket) {
230 close(fd);
231 link_schedule_reconnect(nlink);
232 return;
233 }
234
235 nlink->status = SL_CONNECTED;
236
237 authenticate_link(nlink);
238 } else {
239 if (errno != EINPROGRESS) {
240 nterface_log(ndl, NL_INFO, "SL: Couldn't connect to %s:%d", nlink->hostname->content, nlink->port);
241 link_schedule_reconnect(nlink);
242 return;
243 }
244
245 nlink->socket = esocket_add(fd, ESOCKET_OUTGOING_TCP, &sl_events, BLANK_TOKEN);
246 if(!nlink->socket) {
247 close(fd);
248 link_schedule_reconnect(nlink);
249 return;
250 }
251
252 nlink->status = SL_CONNECTING;
253 }
254
255}
256
257void authenticate_link(struct slink *nlink) {
258}
259
260void link_schedule_reconnect(void *vlink) {
261 struct slink *nlink = (struct slink *)vlink;
262
263 if(nlink->reconnect)
264 deleteschedule(nlink->reconnect, &connect_link, nlink);
265 nlink->reconnect = scheduleoneshot(time(NULL) + RECONNECT_DURATION, &connect_link, nlink);
266}
267
268void disconnect_link(struct slink *nlink) {
269 if(nlink->status == SL_IDLE)
270 return;
271
272 nlink->status = SL_IDLE;
273
274 transport_disconnect(otsl, nlink->socket->fd);
275
276 nlink->socket = NULL;
277
278 link_schedule_reconnect(nlink);
279}
280
281struct slink *find_s_l_from_fd(int fd) {
282 int i = 0;
283 for(;i<link_count;i++)
284 if((slinks[i].status != SL_IDLE) && (fd == slinks[i].socket->fd))
285 return &slinks[i];
286
287 return NULL;
288}
289
290void s_l_buffer_connect_event(struct esocket *socket) {
291 struct slink *nlink = find_s_l_from_fd(socket->fd);
292 if(!nlink) {
293 nterface_log(ndl, NL_ERROR, "SL: Unable to find service link from fd!");
294 return;
295 }
296
297 nterface_log(ndl, NL_INFO, "SL: Connected to %s:%d", nlink->hostname->content, nlink->port);
298 nlink->status = SL_CONNECTED;
299 authenticate_link(nlink);
300}
301
302int s_l_transport_line_event(struct request *request, char *buf) {
303 int i;
304
305 for(i=0;i<service_count;i++)
306 if(&services[i] == request->service) {
307 int j;
308 for(j=0;j<link_count;j++) {
309 if((slinks[j].status == SL_AUTHENTICATED) && (slinks[j].tagid == services[i].tag)) {
310 request->output.tag = slinks[j].socket->fd;
311 if(request->service->realname) {
312 return esocket_write_line(slinks[j].socket, "%s,%d,%s", request->service->realname->content, request->output.token, buf);
313 } else {
314 return esocket_write_line(slinks[j].socket, "%s,%d,%s", request->service->service->content, request->output.token, buf);
315 }
316 }
317 }
318 return 1;
319 }
320
321 return 1;
322}
323
324void s_l_transport_disconnect_event(struct request *req) {
325 /* we can't do much about this, request has already gone into oblivion */
326}
327
328int s_l_buffer_line_event(struct esocket *sock, char *newline) {
329 struct slink *vlink = find_s_l_from_fd(sock->fd);
330 char *response, *theirnonceh = NULL, hexbuf[NONCE_LEN * 2 + 1];
331
332 if(!vlink) {
333 nterface_log(ndl, NL_ERROR, "SL: Unable to find service link from FD!");
334 return 1;
335 }
336
337 switch(vlink->status) {
338 case SL_CONNECTED:
339 if(strcasecmp(newline, ANTI_FULL_VERSION)) {
340 int ret;
341 nterface_log(ndl, NL_WARNING, "SL: Dropped connection with %s:%d due to protocol version mismatch.", vlink->hostname->content, vlink->port);
342
343 ret = esocket_write_line(vlink->socket, "service_link " PROTOCOL_VERSION);
344 if(!ret)
345 esocket_disconnect_when_complete(vlink->socket);
346 return ret;
347 } else {
348 vlink->status = SL_VERSIONED;
349 return esocket_write_line(vlink->socket, "service_link " PROTOCOL_VERSION);
350 }
351 break;
352 case SL_AUTHENTICATED:
353 finish_request(otsl, newline);
354 break;
355 case SL_VERSIONED:
356 for(response=newline;*response;response++) {
357 if((*response == ' ') && (*(response + 1))) {
358 *response = '\0';
359 theirnonceh = response + 1;
360 break;
361 }
362 }
363 if(!theirnonceh || (strlen(theirnonceh) != 32) || !hex_to_int(theirnonceh, vlink->theirnonce, sizeof(vlink->theirnonce))) {
364 nterface_log(ndl, NL_WARNING, "SL: Dropped connection with %s:%d due to protocol error.", vlink->hostname->content, vlink->port);
365 return 1;
366 }
367
368 if(!generate_nonce(vlink->nonce, 0)) {
369 nterface_log(ndl, NL_ERROR, "SL: Unable to generate nonce!");
370 return 1;
371 }
372
373 if(!memcmp(vlink->nonce, vlink->theirnonce, sizeof(vlink->theirnonce))) {
374 nterface_log(ndl, NL_WARNING, "SL: Dropped connection with %s:%d due to identical nonces (this SHOULD be impossible).", vlink->hostname->content, vlink->port);
375 return 1;
376 }
377
378 vlink->status = SL_AUTHENTICATING;
379 return esocket_write_line(vlink->socket, "%s %s", challenge_response(newline, vlink->password->content), int_to_hex(vlink->nonce, hexbuf, 16));
380 break;
381 case SL_AUTHENTICATING:
382 vlink->status = SL_AUTHENTICATED;
383 nterface_log(ndl, NL_INFO, "SL: Authenticated with %s:%d, switching to crypted mode", vlink->hostname->content, vlink->port);
384 switch_buffer_mode(sock, vlink->password->content, vlink->nonce, vlink->theirnonce);
385 break;
386 }
387 return 0;
388}
389
390void s_l_buffer_disconnect_event(struct esocket *socket) {
391 struct slink *vlink = find_s_l_from_fd(socket->fd);
392 if(!vlink) {
393 nterface_log(ndl, NL_ERROR, "SL: Unable to find socket in disconnect event!");
394 return;
395 }
396
397 if(vlink->status == SL_CONNECTING) {
398 nterface_log(ndl, NL_WARNING, "SL: Connection failed (%s:%d), retrying in %d seconds.", vlink->hostname->content, vlink->port, RECONNECT_DURATION);
399 } else {
400 nterface_log(ndl, NL_INFO, "SL: Connection closed (%s:%d), reconnecting in %d seconds.", vlink->hostname->content, vlink->port, RECONNECT_DURATION);
401 }
402
403 disconnect_link(vlink);
404}