1 /*
2 * aprsc
3 *
4 * (c) Heikki Hannikainen, OH7LZB <hessu@hes.iki.fi>
5 *
6 * This program is licensed under the BSD license, which can be found
7 * in the file LICENSE.
8 *
9 */
10
11 /*
12 * parse_qc.c: Process the APRS-IS Q construct
13 */
14
15 #define _GNU_SOURCE
16 #include <string.h>
17 #include <stdio.h>
18 #include <ctype.h>
19
20 #include "parse_qc.h"
21 #include "incoming.h"
22 #include "clientlist.h"
23 #include "config.h"
24 #include "hlog.h"
25
26 /*
27 * Check if a callsign is good for a Q construct
28 * (valid APRS-IS callsign)
29 */
30
check_invalid_q_callsign(const char * call,int len)31 int check_invalid_q_callsign(const char *call, int len)
32 {
33 const char *p = call;
34 const char *e = call + len;
35
36 //hlog(LOG_DEBUG, "check_invalid_q_callsign: '%.*s'", len, call);
37
38 /* either length of callsign, or length of IPv6 address in hex (128/4) */
39 if ((len > CALLSIGNLEN_MAX || len < 1) && len != 32)
40 return -1;
41
42 while (p < e) {
43 if ((!isalnum(*p)) && *p != '-')
44 return -1;
45 p++;
46 }
47
48 return 0;
49 }
50
51 /*
52 * q_dropcheck contains the last big block of the Q construct algorithm
53 * (All packets with q constructs...) and does rejection and duplicate
54 * dropping.
55 */
56
57 #define MAX_Q_CALLS 64
58
q_dropcheck(struct client_t * c,const char * pdata,char * new_q,int new_q_size,char * via_start,int new_q_len,char q_proto,char q_type,char * q_start,char ** q_replace,char * path_end)59 static int q_dropcheck( struct client_t *c, const char *pdata, char *new_q, int new_q_size, char *via_start,
60 int new_q_len, char q_proto, char q_type, char *q_start,
61 char **q_replace, char *path_end )
62 {
63 char *qcallv[MAX_Q_CALLS+1];
64 int qcallc;
65 char *p;
66 int username_len;
67 int login_in_path = 0;
68 int i, j, l;
69
70 if (q_proto != q_protocol_id && disallow_other_protocol_id) {
71 hlog(LOG_DEBUG, "q: dropping due to q%c%c (wrong Q protocol ID)", q_proto, q_type);
72 return INERR_Q_DISALLOW_PROTOCOL; /* drop the packet */
73 }
74
75 /* If disallow_unverified is on, drop packets with qAX (packet from unverified client) */
76 if (q_type == 'X' && disallow_unverified) {
77 hlog(LOG_DEBUG, "q: dropping due to q%c%c (unverified client)", q_proto, q_type);
78 return INERR_Q_QAX; /* drop the packet */
79 }
80
81 /*
82 * if ,qAZ, is the q construct:
83 * {
84 * Dump to the packet to the reject log
85 * Quit processing the packet
86 * }
87 */
88
89 if (q_type == 'Z') {
90 /* qAZ is for packets from client to server, to prevent redistribution */
91 hlog(LOG_DEBUG, "q: dropping due q_type %c%c", q_proto, q_type);
92 return INERR_Q_QAZ; /* drop the packet */
93 }
94
95 /*
96 * Produce an array of pointers pointing to each callsign in the path
97 * after the q construct
98 */
99 qcallc = 0;
100 if (q_start) {
101 p = q_start + 4;
102 while (qcallc < MAX_Q_CALLS && p < path_end) {
103 while (p < path_end && *p == ',')
104 p++;
105 if (p == path_end)
106 break;
107 qcallv[qcallc++] = p;
108 while (p < path_end && *p != ',')
109 p++;
110 }
111 qcallv[qcallc] = p+1;
112 }
113
114 /*
115 * If ,SERVERLOGIN is found after the q construct:
116 * {
117 * Dump to the loop log with the sender's IP address for identification
118 * Quit processing the packet
119 * }
120 *
121 * (note: if serverlogin is 'XYZ-1', it must not match XYZ-12, so we need to
122 * match against ,SERVERLOGIN, or ,SERVERLOGIN:)
123 */
124
125 if (q_start) {
126 p = memmem(q_start+4, path_end-q_start-4, serverid, serverid_len);
127 if (p && *(p-1) == ',' && ( *(p+serverid_len) == ',' || p+serverid_len == path_end || *(p+serverid_len) == ':' )) {
128 /* TODO: The reject log should really log the offending packet */
129 hlog(LOG_DEBUG, "q: dropping due to my callsign appearing in path");
130 return INERR_Q_QPATH_MYCALL; /* drop the packet */
131 }
132 }
133
134 /*
135 * 1)
136 * If a callsign-SSID is found twice in the q construct:
137 * {
138 * Dump to the loop log with the sender's IP address for identification
139 * Quit processing the packet
140 * }
141 *
142 * 2)
143 * If a verified login other than this login is found in the q construct
144 * and that login is not allowed to have multiple verified connects (the
145 * IPADDR of an outbound connection is considered a verified login):
146 * {
147 * Dump to the loop log with the sender's IP address for identification
148 * Quit processing the packet
149 * }
150 *
151 * 3)
152 * If the packet is from an inbound port and the login is found after the q construct but is not the LAST VIACALL:
153 * {
154 * Dump to the loop log with the sender's IP address for identification
155 * Quit processing the packet
156 * }
157 *
158 */
159 username_len = strlen(c->username);
160 for (i = 0; i < qcallc; i++) {
161 l = qcallv[i+1] - qcallv[i] - 1;
162 /* 1) */
163 for (j = i + 1; j < qcallc; j++) {
164 /* this match is case sensitive in javaprssrvr, so that's what we'll do */
165 if (l == qcallv[j+1] - qcallv[j] - 1 && memcmp(qcallv[i], qcallv[j], l) == 0) {
166 /* TODO: The reject log should really log the offending packet */
167 hlog(LOG_DEBUG, "q: dropping due to callsign-SSID '%.*s' found twice after Q construct", l, qcallv[i]);
168 return INERR_Q_QPATH_CALL_TWICE;
169 }
170 }
171 if (l == username_len && strncasecmp(qcallv[i], c->username, username_len) == 0) {
172 /* ok, login is client's login, handle step 3) */
173 login_in_path = 1;
174 if (c->state == CSTATE_CONNECTED &&
175 (c->flags & CLFLAGS_INPORT) &&
176 (i != qcallc - 1)) {
177 /* 3) hits: from an inbound connection, client login found in path,
178 * but is not the last viacall
179 * TODO: should dump...
180 */
181 /* TODO: The reject log should really log the offending packet */
182 hlog(LOG_DEBUG, "q: dropping due to login callsign %s not being the last viacall after Q construct", c->username);
183 return INERR_Q_PATH_LOGIN_NOT_LAST;
184 }
185 } else if (clientlist_check_if_validated_client(qcallv[i], l) != -1) {
186 /* 2) hits: TODO: should dump to a loop log */
187 /* TODO: The reject log should really log the offending packet */
188 hlog(LOG_DEBUG, "q: dropping due to callsign '%.*s' after Q construct being logged in on another socket, arrived from %s", l, qcallv[i], c->username);
189 return INERR_Q_PATH_CALL_IS_LOCAL_CLIENT;
190 } else if (check_invalid_q_callsign(qcallv[i], l) != 0) {
191 hlog(LOG_DEBUG, "q: dropping due to callsign '%.*s' after Q construct being invalid as an APRS-IS server name, arrived from %s", l, qcallv[i], c->username);
192 return INERR_Q_PATH_CALL_IS_INVALID;
193 }
194
195 if (q_type == 'U' && memcmp(qcallv[i], pdata, l) == 0 && pdata[l] == '>') {
196 hlog(LOG_DEBUG, "q: dropping due to callsign '%.*s' after qAU also being srccall", l, qcallv[i]);
197 return INERR_Q_QAU_PATH_CALL_IS_SRCCALL;
198 }
199 }
200
201 /*
202 * If trace is on, the q construct is qAI, or the FROMCALL is on the server's trace list:
203 * {
204 * If the packet is from a verified port where the login is not found after the q construct:
205 * Append ,login
206 * else if the packet is from an outbound connection
207 * Append ,IPADDR
208 *
209 * Append ,SERVERLOGIN
210 * }
211 */
212
213 if (q_type == 'I') {
214 /* we replace the existing Q construct with a regenerated one */
215 *q_replace = q_start+1;
216
217 //hlog(LOG_DEBUG, "qAI: not INPORT, appending ,username %s", c->username);
218 /* copy over existing qAI trace */
219 new_q_len = path_end - q_start - 1;
220 //hlog(LOG_DEBUG, "qAI replacing, new_q_len %d", new_q_len);
221 if (new_q_len > new_q_size) {
222 /* ouch, memcpy would run over the buffer */
223 /* TODO: The reject log should really log the offending packet */
224 hlog(LOG_DEBUG, "q: dropping due to buffer being too tight");
225 return INERR_Q_NEWQ_BUFFER_SMALL;
226 }
227 memcpy(new_q, q_start+1, new_q_len);
228
229 //hlog(LOG_DEBUG, "qAI first memcpy done, new_q_len %d, q_replace %d, new_q %.*s", new_q_len, *q_replace, new_q_len, new_q);
230
231 /* If the packet is from a verified port where the login is not found after the q construct,
232 * append ,login
233 * - but not if this is an UDP core peer, and we don't know the username */
234 if (c->validated && !login_in_path && c->state != CSTATE_COREPEER) {
235 /* Append ,login */
236 //hlog(LOG_DEBUG, "qAI: from validated client, login not in path, appending ,username %s", c->username);
237 new_q_len += snprintf(new_q + new_q_len, new_q_size - new_q_len, ",%s", c->username);
238 }
239 //hlog(LOG_DEBUG, "qAI append done, new_q_len %d, new_q_size %d, q_replace %d, going to append %d more", new_q_len, new_q_size, *q_replace, strlen(serverid)+1);
240
241 if (new_q_size - new_q_len < 20) {
242 hlog(LOG_DEBUG, "q dropping due to buffer being too tight when appending my login for qAI");
243 return INERR_Q_NEWQ_BUFFER_SMALL;
244 }
245
246 /* Append ,SERVERLOGIN */
247 new_q_len += snprintf(new_q + new_q_len, new_q_size - new_q_len, ",%s", serverid);
248 //hlog(LOG_DEBUG, "qAI: complete, new_q %s", new_q);
249 }
250
251 return new_q_len;
252 }
253
254
255 /*
256 * Parse, and possibly generate a Q construct.
257 * http://www.aprs-is.net/q.htm
258 * http://www.aprs-is.net/qalgorithm.htm
259 *
260 * Called by incoming.c, runs in the worker thread's context.
261 * Threadsafe.
262 *
263 * 1) figure out where a (possibly) existing Q construct is
264 * 2) if it exists, we might need to modify it
265 * 3) we might have to append a new Q construct if one does not exist
266 * 4) we might have to append our server ID to the path
267 * 5) we might have to append the hexadecimal IP address of an UDP peer
268 *
269 * This function is a bit too long, should probably split a bit, and
270 * reuse some code snippets.
271 */
272
q_process(struct client_t * c,const char * pdata,char * new_q,int new_q_size,char * via_start,char ** path_end,int pathlen,char ** new_q_start,char ** q_replace,int originated_by_client)273 int q_process(struct client_t *c, const char *pdata, char *new_q, int new_q_size, char *via_start,
274 char **path_end, int pathlen, char **new_q_start, char **q_replace,
275 int originated_by_client)
276 {
277 char *q_start = NULL; /* points to the , before the Q construct */
278 char *q_nextcall = NULL; /* points to the , after the Q construct */
279 char *q_nextcall_end = NULL; /* points to the , after the callsign right after the Q construct */
280 int new_q_len = 0; /* the length of a newly generated Q construct */
281 char q_proto = 0; /* parsed Q construct protocol character (A for APRS) */
282 char q_type = 0; /* parsed Q construct type character */
283
284 /*
285 All packets
286 {
287 Place into TNC-2 format
288 If a q construct is last in the path (no call following the qPT)
289 delete the qPT
290 }
291 */
292
293 // fprintf(stderr, "q_process\n");
294 q_start = memmem(via_start, *path_end - via_start, ",q", 2);
295 if (q_start) {
296 // fprintf(stderr, "\tfound existing q construct\n");
297 /* there is an existing Q construct, check for a callsign after it */
298 q_nextcall = memchr(q_start + 1, ',', *path_end - q_start - 1);
299 if (!q_nextcall) {
300 // fprintf(stderr, "\tno comma after Q construct, ignoring and overwriting construct\n");
301 *path_end = q_start;
302 } else if (q_nextcall - q_start != 4) {
303 /* does not fit qPT */
304 // fprintf(stderr, "\tlength of Q construct is not 3 characters\n");
305 *path_end = q_start;
306 } else {
307 /* parse the q construct itself */
308 q_proto = *(q_start + 2);
309 q_type = *(q_start + 3);
310
311 /* check for callsign following qPT */
312 q_nextcall++; /* now points to the callsign */
313 q_nextcall_end = q_nextcall;
314 while (q_nextcall_end < *path_end && *q_nextcall_end != ',' && *q_nextcall_end != ':')
315 q_nextcall_end++;
316 if (q_nextcall == q_nextcall_end) {
317 // fprintf(stderr, "\tno callsign after Q construct, ignoring and overwriting construct\n");
318 *path_end = q_start;
319 q_proto = q_type = 0; /* for the further code: we do not have a Qc */
320 }
321 /* it's OK to have more than one callsign after qPT */
322 }
323 } else {
324 /* if the packet's srccall == login, replace digipeater path with
325 * TCPIP* and insert Q construct
326 */
327 //hlog(LOG_DEBUG, "no q found");
328 if (originated_by_client && !(c->flags & CLFLAGS_UDPSUBMIT)) {
329 // where to replace from
330 *q_replace = via_start;
331 //hlog(LOG_DEBUG, "inserting TCPIP,qAC... starting at %s", *q_replace);
332 if (c->validated)
333 return snprintf(new_q, new_q_size, ",TCPIP*,q%cC,%s", q_protocol_id, serverid);
334 else
335 return snprintf(new_q, new_q_size, ",TCPXX*,q%cX,%s", q_protocol_id, serverid);
336 }
337 }
338
339 /* If the packet's srccall == login,
340 * put in a TCPIP* path element and append Q construct
341 */
342 if (c->validated && originated_by_client && c->flags & CLFLAGS_INPORT && q_type != 'I' && !(c->flags & CLFLAGS_UDPSUBMIT)) {
343 // where to replace from
344 *q_replace = via_start;
345 //hlog(LOG_DEBUG, "inserting TCPIP,qAC... starting at %s", *q_replace);
346 return snprintf(new_q, new_q_size, ",TCPIP*,q%cC,%s", q_protocol_id, serverid);
347 }
348
349 // fprintf(stderr, "\tstep 2...\n");
350
351 /* ok, we now either have found an existing Q construct + the next callsign,
352 * or have eliminated an outright invalid Q construct.
353 */
354
355 /*
356 * All packets from an inbound connection that would normally be passed per current validation algorithm:
357 */
358
359 if (c->state == CSTATE_CONNECTED &&
360 (c->flags & CLFLAGS_INPORT)) {
361 /*
362 * If the packet entered the server from an UDP port:
363 * {
364 * if a q construct exists in the packet
365 * Replace the q construct with ,qAU,SERVERLOGIN
366 * else
367 * Append ,qAU,SERVERLOGIN
368 * Quit q processing
369 * }
370 */
371 if (c->flags & CLFLAGS_UDPSUBMIT) {
372 //fprintf(stderr, "\tUDP packet\n");
373 if (q_proto) {
374 /* a q construct with a exists in the packet,
375 * Replace the q construct with ,qAU,SERVERLOGIN
376 */
377 *path_end = q_start;
378 }
379 /* Append ,qAU,SERVERLOGIN */
380 /* Add TCPIP* in the end of the path only if it's not there already */
381 if (pathlen > 7 && strncmp(*path_end-7, ",TCPIP*", 7) == 0)
382 return snprintf(new_q, new_q_size, ",q%cU,%s", q_protocol_id, serverid);
383 else
384 return snprintf(new_q, new_q_size, ",TCPIP*,q%cU,%s", q_protocol_id, serverid);
385 }
386
387 /*
388 * If the packet entered the server from an unverified connection AND
389 * the FROMCALL equals the client login AND the header has been
390 * successfully converted to TCPXX format (per current validation algorithm):
391 * {
392 * (All packets not deemed "OK" from an unverified connection should be dropped.)
393 * if a q construct with a single call exists in the packet
394 * Replace the q construct with ,qAX,SERVERLOGIN
395 * else if more than a single call exists after the q construct
396 * Invalid header, drop packet as error
397 * else
398 * Append ,qAX,SERVERLOGIN
399 * Quit q processing
400 * }
401 */
402 if ((!c->validated) && originated_by_client) {
403 // fprintf(stderr, "\tunvalidated client sends packet originated by itself\n");
404 // FIXME: how to check if TCPXX conversion is done? Just assume?
405 if (q_proto && q_nextcall_end == *path_end) {
406 /* a q construct with a single call exists in the packet,
407 * Replace the q construct with ,qAX,SERVERLOGIN
408 */
409 *path_end = via_start;
410 return snprintf(new_q, new_q_size, ",TCPXX*,q%cX,%s", q_protocol_id, serverid);
411 } else if (q_proto && q_nextcall_end < *path_end) {
412 /* more than a single call exists after the q construct,
413 * invalid header, drop the packet as error
414 */
415 return INERR_Q_NONVAL_MULTI_Q_CALLS;
416 } else {
417 /* Append ,qAX,SERVERLOGIN (well, javaprssrvr replaces the via path too) */
418 *path_end = via_start;
419 return snprintf(new_q, new_q_size, ",TCPXX*,q%cX,%s", q_protocol_id, serverid);
420 }
421 }
422
423 if (c->validated && (c->flags & CLFLAGS_CLIENTONLY) && originated_by_client) {
424 if (q_start)
425 *path_end = q_start;
426 else
427 *path_end = via_start;
428 return snprintf(new_q, new_q_size, ",q%cC,%s", q_protocol_id, serverid);
429 }
430
431 /* OLD:
432 * If the packet entered the server from a verified client-only connection
433 * AND the FROMCALL does not match the login:
434 * {
435 * if a q construct exists in the packet
436 * if the q construct is at the end of the path AND it equals ,qAR,login
437 * Replace qAR with qAo
438 * else if the path is terminated with ,login,I
439 * Replace ,login,I with qAo,login
440 * else
441 * Append ,qAO,login
442 * Skip to "All packets with q constructs"
443 * }
444 *
445 * NOW:
446 * If the packet entered the server from a verified client-only connection
447 * AND the FROMCALL does not match the login:
448 * {
449 * if a q construct exists in the path
450 * if the q construct equals ,qAR,callsignssid or ,qAr,callsignssid
451 * Replace qAR or qAr with qAo
452 * else if the q construct equals ,qAS,callsignssid
453 * Replace qAS with qAO
454 * else if the q construct equals ,qAC,callsignssid and callsignssid is not equal to the servercall or login
455 * Replace qAC with qAO
456 * else if the path is terminated with ,callsignssid,I
457 * Replace ,callsignssid,I with qAo,callsignssid
458 * else
459 * Append ,qAO,login
460 * Skip to "All packets with q constructs"
461 * }
462 */
463 if (c->validated && (c->flags & CLFLAGS_CLIENTONLY) && !originated_by_client) {
464 // fprintf(stderr, "\tvalidated client sends sends packet originated by someone else\n");
465 /* if a q construct exists in the packet */
466 int add_qAO = 1;
467 if (q_proto) {
468 // fprintf(stderr, "\thas q construct\n");
469 /* if the q construct equals ,qAR,callsignssid or ,qAr,callsignssid */
470 if (q_type == 'R' || q_type == 'r') {
471 /* Replace qAR with qAo */
472 *(q_start + 3) = q_type = 'o';
473 // fprintf(stderr, "\treplaced qAR with qAo\n");
474 } else if (q_type == 'S') {
475 /* Replace qAS with qAO */
476 *(q_start + 3) = q_type = 'O';
477 // fprintf(stderr, "\treplaced qAS with qAO\n");
478 } else if (q_type == 'C') {
479 // FIXME: should also check callsignssid to servercall & login
480 /* Replace qAC with qAO */
481 *(q_start + 3) = q_type = 'O';
482 // fprintf(stderr, "\treplaced qAC with qAO\n");
483 } else {
484 // What? Dunno.
485 }
486 /* Not going to modify the construct, update pointer to it */
487 *new_q_start = q_start + 1;
488 add_qAO = 0;
489 } else if (pathlen > 2 && *(*path_end -1) == 'I' && *(*path_end -2) == ',') {
490 hlog_packet(LOG_DEBUG, pdata, pathlen, "path has ,I in the end: ");
491 /* the path is terminated with ,I - lookup previous callsign in path */
492 char *p = *path_end - 3;
493 while (p > via_start && *p != ',')
494 p--;
495 if (*p == ',') {
496 const char *prevcall = p+1;
497 const char *prevcall_end = *path_end - 2;
498 //hlog(LOG_DEBUG, "previous callsign is %.*s", (int)(prevcall_end - prevcall), prevcall);
499 /* if the path is terminated with ,login,I */
500 // TODO: Should validate that prevcall is a nice callsign
501 if (1) {
502 /* Replace ,login,I with qAo,previouscall */
503 *path_end = p;
504 new_q_len = snprintf(new_q, new_q_size, ",q%co,%.*s",
505 q_protocol_id, (int)(prevcall_end - prevcall), prevcall);
506 q_proto = q_protocol_id;
507 q_type = 'o';
508 add_qAO = 0;
509 }
510 }
511 }
512
513 if (add_qAO) {
514 /* Append ,qAO,login */
515 new_q_len = snprintf(new_q, new_q_size, ",q%cO,%s", q_protocol_id, c->username);
516 q_proto = q_protocol_id;
517 q_type = 'O';
518 }
519
520 /* Skip to "All packets with q constructs" */
521 return q_dropcheck(c, pdata, new_q, new_q_size, via_start, new_q_len, q_proto, q_type, q_start, q_replace, *path_end);
522 }
523
524 /*
525 * If a q construct exists in the header:
526 * Skip to "All packets with q constructs"
527 */
528 if (q_proto) {
529 // fprintf(stderr, "\texisting q construct\n");
530 /* Not going to modify the construct, update pointer to it */
531 *new_q_start = q_start + 1;
532 /* Skip to "All packets with q constructs" */
533 return q_dropcheck(c, pdata, new_q, new_q_size, via_start, new_q_len, q_proto, q_type, q_start, q_replace, *path_end);
534 }
535
536 /* At this point we have packets which do not have Q constructs, and
537 * are either (from validated clients && originated by client)
538 * or (from unvalidated client && not originated by the client)
539 */
540
541 /*
542 * If header is terminated with ,I:
543 * {
544 * If the VIACALL preceding the ,I matches the login:
545 * Change from ,VIACALL,I to ,qAR,VIACALL
546 * Else
547 * Change from ,VIACALL,I to ,qAr,VIACALL
548 * }
549 * Else If the FROMCALL matches the login:
550 * {
551 * Append ,qAC,SERVERLOGIN
552 * Quit q processing
553 * }
554 * Else
555 * Append ,qAS,login
556 * Skip to "All packets with q constructs"
557 */
558 if (pathlen > 2 && *(*path_end -1) == 'I' && *(*path_end -2) == ',') {
559 // fprintf(stderr, "\tpath has ,I in the end\n");
560 /* the path is terminated with ,I - lookup previous callsign in path */
561 char *p = *path_end - 3;
562 while (p > via_start && *p != ',')
563 p--;
564 if (*p == ',') {
565 const char *prevcall = p+1;
566 const char *prevcall_end = *path_end - 2;
567 // fprintf(stderr, "\tprevious callsign is %.*s\n", prevcall_end - prevcall, prevcall);
568 /* if the path is terminated with ,login,I */
569 if (strlen(c->username) == prevcall_end - prevcall && strncasecmp(c->username, prevcall, prevcall_end - prevcall) == 0) {
570 /* Replace ,login,I with qAR,login */
571 *path_end = p;
572 new_q_len = snprintf(new_q, new_q_size, ",q%cR,%s", q_protocol_id, c->username);
573 q_proto = q_protocol_id;
574 q_type = 'R';
575 } else {
576 /* Replace ,VIACALL,I with qAr,VIACALL */
577 *path_end = p;
578 new_q_len = snprintf(new_q, new_q_size, ",q%cr,%.*s", q_protocol_id, (int)(prevcall_end - prevcall), prevcall);
579 q_proto = q_protocol_id;
580 q_type = 'r';
581 }
582 } else {
583 /* Undefined by the algorithm - there was no VIACALL */
584 return INERR_Q_I_NO_VIACALL;
585 }
586 } else if (originated_by_client) {
587 /* FROMCALL matches the login */
588 /* Add TCPIP* in the end of the path only if it's not there already */
589 if (pathlen > 7 && strncmp(*path_end-7, ",TCPIP*", 7) == 0)
590 return snprintf(new_q, new_q_size, ",qAC,%s", serverid);
591 else
592 return snprintf(new_q, new_q_size, ",TCPIP*,qAC,%s", serverid);
593 } else {
594 /* Append ,qAS,login */
595 new_q_len = snprintf(new_q, new_q_size, ",q%cS,%s", q_protocol_id, c->username);
596 q_proto = q_protocol_id;
597 q_type = 'S';
598 }
599 /* Skip to "All packets with q constructs" */
600 return q_dropcheck(c, pdata, new_q, new_q_size, via_start, new_q_len, q_proto, q_type, q_start, q_replace, *path_end);
601 }
602
603 /*
604 * If packet entered the server from an outbound connection (to
605 * another server's port 1313, for instance) and no q construct
606 * exists in the header:
607 * {
608 * If header is terminated with ,I:
609 * Change from ,VIACALL,I to ,qAr,VIACALL
610 * Else
611 * Append ,qAS,IPADDR (IPADDR is an 8 character hex
612 * representation of the IP address of the remote server)
613 * }
614 * Untested at the time of implementation (no uplink support yet)
615 */
616
617 if (!q_proto && (c->flags & CLFLAGS_UPLINKPORT)) {
618 if (pathlen > 2 && *(*path_end -1) == 'I' && *(*path_end -2) == ',') {
619 // fprintf(stderr, "\tpath has ,I in the end\n");
620 /* the path is terminated with ,I - lookup previous callsign in path */
621 char *p = *path_end - 3;
622 while (p > via_start && *p != ',')
623 p--;
624 if (*p == ',') {
625 const char *prevcall = p+1;
626 const char *prevcall_end = *path_end - 2;
627 // fprintf(stderr, "\tprevious callsign is %.*s\n", prevcall_end - prevcall, prevcall);
628 /* Replace ,VIACALL,I with qAr,VIACALL */
629 *path_end = p;
630 new_q_len = snprintf(new_q, new_q_size, ",q%cr,%.*s", q_protocol_id, (int)(prevcall_end - prevcall), prevcall);
631 q_proto = q_protocol_id;
632 q_type = 'r';
633 } else {
634 /* Undefined by the algorithm - there was no VIACALL */
635 return INERR_Q_I_NO_VIACALL;
636 }
637 } else {
638 /* Append ,qAS,IPADDR (IPADDR is an 8 character hex representation
639 * of the IP address of the remote server)
640 */
641 new_q_len = snprintf(new_q, new_q_size, ",q%cS,%s", q_protocol_id, c->addr_hex);
642 q_proto = q_protocol_id;
643 q_type = 'S';
644 }
645 }
646
647 /* If we haven't generated a new Q construct, return a pointer to the existing one */
648 if (!new_q_len) {
649 if (q_start == NULL)
650 hlog(LOG_ERR, "q: Did not find or generate a Q construct (from client %s fd %d): %s", c->username, c->fd, pdata);
651 *new_q_start = q_start + 1;
652 }
653
654 return q_dropcheck(c, pdata, new_q, new_q_size, via_start, new_q_len, q_proto, q_type, q_start, q_replace, *path_end);
655 }
656
657