1 //
2 // This file is part of Dire Wolf, an amateur radio packet TNC.
3 //
4 // Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2020 John Langner, WB2OSZ
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19
20
21 /*------------------------------------------------------------------
22 *
23 * Module: server.c
24 *
25 * Purpose: Provide service to other applications via "AGW TCPIP Socket Interface".
26 *
27 * Input:
28 *
29 * Outputs:
30 *
31 * Description: This provides a TCP socket for communication with a client application.
32 * It implements a subset of the AGW socket interface.
33 *
34 * Commands from application recognized:
35 *
36 * 'R' Request for version number.
37 * (See below for response.)
38 *
39 * 'G' Ask about radio ports.
40 * (See below for response.)
41 *
42 * 'g' Capabilities of a port. (new in 0.8)
43 * (See below for response.)
44 *
45 * 'k' Ask to start receiving RAW AX25 frames.
46 *
47 * 'm' Ask to start receiving Monitor AX25 frames.
48 * Enables sending of U, I, S, and T messages to client app.
49 *
50 * 'V' Transmit UI data frame.
51 *
52 * 'H' Report recently heard stations. Not implemented yet.
53 *
54 * 'K' Transmit raw AX.25 frame.
55 *
56 * 'X' Register CallSign
57 *
58 * 'x' Unregister CallSign
59 *
60 * 'y' Ask Outstanding frames waiting on a Port (new in 1.2)
61 *
62 * 'Y' How many frames waiting for transmit for a particular station (new in 1.5)
63 *
64 * 'C' Connect, Start an AX.25 Connection (new in 1.4)
65 *
66 * 'v' Connect VIA, Start an AX.25 circuit thru digipeaters (new in 1.4)
67 *
68 * 'c' Connection with non-standard PID (new in 1.4)
69 *
70 * 'D' Send Connected Data (new in 1.4)
71 *
72 * 'd' Disconnect, Terminate an AX.25 Connection (new in 1.4)
73 *
74 *
75 * A message is printed if any others are received.
76 *
77 * TODO: Should others be implemented?
78 *
79 *
80 * Messages sent to client application:
81 *
82 * 'R' Reply to Request for version number.
83 * Currently responds with major 1, minor 0.
84 *
85 * 'G' Reply to Ask about radio ports.
86 *
87 * 'g' Reply to capabilities of a port. (new in 0.8)
88 *
89 * 'K' Received AX.25 frame in raw format.
90 * (Enabled with 'k' command.)
91 *
92 * 'U' Received AX.25 "UI" frames in monitor format.
93 * (Enabled with 'm' command.)
94 *
95 * 'I' Received AX.25 "I" frames in monitor format. (new in 1.6)
96 * (Enabled with 'm' command.)
97 *
98 * 'S' Received AX.25 "S" and "U" (other than UI) frames in monitor format. (new in 1.6)
99 * (Enabled with 'm' command.)
100 *
101 * 'T' Own Transmitted AX.25 frames in monitor format. (new in 1.6)
102 * (Enabled with 'm' command.)
103 *
104 * 'y' Outstanding frames waiting on a Port (new in 1.2)
105 *
106 * 'Y' How many frames waiting for transmit for a particular station (new in 1.5)
107 *
108 * 'C' AX.25 Connection Received (new in 1.4)
109 *
110 * 'D' Connected AX.25 Data (new in 1.4)
111 *
112 * 'd' Disconnected (new in 1.4)
113 *
114 *
115 *
116 * References: AGWPE TCP/IP API Tutorial
117 * http://uz7ho.org.ua/includes/agwpeapi.htm
118 *
119 * Getting Started with Winsock
120 * http://msdn.microsoft.com/en-us/library/windows/desktop/bb530742(v=vs.85).aspx
121 *
122 *
123 * Major change in 1.1:
124 *
125 * Formerly a single client was allowed.
126 * Now we can have multiple concurrent clients.
127 *
128 *---------------------------------------------------------------*/
129
130
131 /*
132 * Native Windows: Use the Winsock interface.
133 * Linux: Use the BSD socket interface.
134 * Cygwin: Can use either one.
135 */
136
137 #include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
138
139 #if __WIN32__
140 #include <winsock2.h>
141 #include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
142 #else
143 #include <stdlib.h>
144 #include <sys/types.h>
145 #include <sys/ioctl.h>
146 #include <sys/socket.h>
147 #include <netinet/in.h>
148 #include <errno.h>
149 #endif
150
151 #include <unistd.h>
152 #include <stdio.h>
153 #include <assert.h>
154 #include <string.h>
155 #include <time.h>
156 #include <ctype.h>
157 #include <stddef.h>
158
159 #include "tq.h"
160 #include "ax25_pad.h"
161 #include "textcolor.h"
162 #include "audio.h"
163 #include "server.h"
164 #include "dlq.h"
165
166
167
168 /*
169 * Previously, we allowed only one network connection at a time to each port.
170 * In version 1.1, we allow multiple concurrent client apps to attach with the AGW network protocol.
171 * The default is a limit of 3 client applications at the same time.
172 * You can increase the limit by changing the line below.
173 * A larger number consumes more resources so don't go crazy by making it larger than needed.
174 */
175
176 #define MAX_NET_CLIENTS 3
177
178 static int client_sock[MAX_NET_CLIENTS];
179 /* File descriptor for socket for */
180 /* communication with client application. */
181 /* Set to -1 if not connected. */
182 /* (Don't use SOCKET type because it is unsigned.) */
183
184 static int enable_send_raw_to_client[MAX_NET_CLIENTS];
185 /* Should we send received packets to client app in raw form? */
186 /* Note that it starts as false for a new connection. */
187 /* the client app must send a command to enable this. */
188
189 static int enable_send_monitor_to_client[MAX_NET_CLIENTS];
190 /* Should we send received packets to client app in monitor form? */
191 /* Note that it starts as false for a new connection. */
192 /* the client app must send a command to enable this. */
193
194
195 // TODO: define in one place, use everywhere.
196 // TODO: Macro to terminate thread when no point to go on.
197
198 #if __WIN32__
199 #define THREAD_F unsigned __stdcall
200 #else
201 #define THREAD_F void *
202 #endif
203
204 static THREAD_F connect_listen_thread (void *arg);
205 static THREAD_F cmd_listen_thread (void *arg);
206
207 /*
208 * Message header for AGW protocol.
209 * Multibyte numeric values require rearranging for big endian cpu.
210 */
211
212 /*
213 * With MinGW version 4.6, obviously x86.
214 * or Linux gcc version 4.9, Linux ARM.
215 *
216 * $ gcc -E -dM - < /dev/null | grep END
217 * #define __ORDER_LITTLE_ENDIAN__ 1234
218 * #define __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
219 * #define __ORDER_PDP_ENDIAN__ 3412
220 * #define __ORDER_BIG_ENDIAN__ 4321
221 * #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
222 *
223 * This is for standard OpenWRT on MIPS.
224 *
225 * #define __ORDER_LITTLE_ENDIAN__ 1234
226 * #define __FLOAT_WORD_ORDER__ __ORDER_BIG_ENDIAN__
227 * #define __ORDER_PDP_ENDIAN__ 3412
228 * #define __ORDER_BIG_ENDIAN__ 4321
229 * #define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
230 *
231 * This was reported for an old Mac with PowerPC processor.
232 * (Newer versions have x86.)
233 *
234 * $ gcc -E -dM - < /dev/null | grep END
235 * #define __BIG_ENDIAN__ 1
236 * #define _BIG_ENDIAN 1
237 */
238
239
240 #if defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
241
242 // gcc >= 4.2 has __builtin_swap32() but might not be compatible with older versions or other compilers.
243
244 #define host2netle(x) ( (((x)>>24)&0x000000ff) | (((x)>>8)&0x0000ff00) | (((x)<<8)&0x00ff0000) | (((x)<<24)&0xff000000) )
245 #define netle2host(x) ( (((x)>>24)&0x000000ff) | (((x)>>8)&0x0000ff00) | (((x)<<8)&0x00ff0000) | (((x)<<24)&0xff000000) )
246
247 #else
248
249 #define host2netle(x) (x)
250 #define netle2host(x) (x)
251
252 #endif
253
254
255 struct agwpe_s {
256 unsigned char portx; /* 0 for first, 1 for second, etc. */
257 unsigned char reserved1;
258 unsigned char reserved2;
259 unsigned char reserved3;
260
261 unsigned char datakind; /* message type, usually written as a letter. */
262 unsigned char reserved4;
263 unsigned char pid;
264 unsigned char reserved5;
265
266 char call_from[10];
267
268 char call_to[10];
269
270 int data_len_NETLE; /* Number of data bytes following. */
271 /* _NETLE suffix is reminder to convert for network byte order. */
272
273 int user_reserved_NETLE;
274 };
275
276
277 static void send_to_client (int client, void *reply_p);
278
279
280 /*-------------------------------------------------------------------
281 *
282 * Name: debug_print
283 *
284 * Purpose: Print message to/from client for debugging.
285 *
286 * Inputs: fromto - Direction of message.
287 * client - client number, 0 .. MAX_NET_CLIENTS-1
288 * pmsg - Address of the message block.
289 * msg_len - Length of the message.
290 *
291 *--------------------------------------------------------------------*/
292
293 static int debug_client = 0; /* Debug option: Print information flowing from and to client. */
294
server_set_debug(int n)295 void server_set_debug (int n)
296 {
297 debug_client = n;
298 }
299
hex_dump(unsigned char * p,int len)300 void hex_dump (unsigned char *p, int len)
301 {
302 int n, i, offset;
303
304 offset = 0;
305 while (len > 0) {
306 n = len < 16 ? len : 16;
307 dw_printf (" %03x: ", offset);
308 for (i=0; i<n; i++) {
309 dw_printf (" %02x", p[i]);
310 }
311 for (i=n; i<16; i++) {
312 dw_printf (" ");
313 }
314 dw_printf (" ");
315 for (i=0; i<n; i++) {
316 dw_printf ("%c", isprint(p[i]) ? p[i] : '.');
317 }
318 dw_printf ("\n");
319 p += 16;
320 offset += 16;
321 len -= 16;
322 }
323 }
324
325 typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t;
326
debug_print(fromto_t fromto,int client,struct agwpe_s * pmsg,int msg_len)327 static void debug_print (fromto_t fromto, int client, struct agwpe_s *pmsg, int msg_len)
328 {
329 char direction [10];
330 char datakind[80];
331 const char *prefix [2] = { "<<<", ">>>" };
332
333 switch (fromto) {
334
335 case FROM_CLIENT:
336 strlcpy (direction, "from", sizeof(direction)); /* from the client application */
337
338 switch (pmsg->datakind) {
339 case 'P': strlcpy (datakind, "Application Login", sizeof(datakind)); break;
340 case 'X': strlcpy (datakind, "Register CallSign", sizeof(datakind)); break;
341 case 'x': strlcpy (datakind, "Unregister CallSign", sizeof(datakind)); break;
342 case 'G': strlcpy (datakind, "Ask Port Information", sizeof(datakind)); break;
343 case 'm': strlcpy (datakind, "Enable Reception of Monitoring Frames", sizeof(datakind)); break;
344 case 'R': strlcpy (datakind, "AGWPE Version Info", sizeof(datakind)); break;
345 case 'g': strlcpy (datakind, "Ask Port Capabilities", sizeof(datakind)); break;
346 case 'H': strlcpy (datakind, "Callsign Heard on a Port", sizeof(datakind)); break;
347 case 'y': strlcpy (datakind, "Ask Outstanding frames waiting on a Port", sizeof(datakind)); break;
348 case 'Y': strlcpy (datakind, "Ask Outstanding frames waiting for a connection", sizeof(datakind)); break;
349 case 'M': strlcpy (datakind, "Send UNPROTO Information", sizeof(datakind)); break;
350 case 'C': strlcpy (datakind, "Connect, Start an AX.25 Connection", sizeof(datakind)); break;
351 case 'D': strlcpy (datakind, "Send Connected Data", sizeof(datakind)); break;
352 case 'd': strlcpy (datakind, "Disconnect, Terminate an AX.25 Connection", sizeof(datakind)); break;
353 case 'v': strlcpy (datakind, "Connect VIA, Start an AX.25 circuit thru digipeaters", sizeof(datakind)); break;
354 case 'V': strlcpy (datakind, "Send UNPROTO VIA", sizeof(datakind)); break;
355 case 'c': strlcpy (datakind, "Non-Standard Connections, Connection with PID", sizeof(datakind)); break;
356 case 'K': strlcpy (datakind, "Send data in raw AX.25 format", sizeof(datakind)); break;
357 case 'k': strlcpy (datakind, "Activate reception of Frames in raw format", sizeof(datakind)); break;
358 default: strlcpy (datakind, "**INVALID**", sizeof(datakind)); break;
359 }
360 break;
361
362 case TO_CLIENT:
363 default:
364 strlcpy (direction, "to", sizeof(direction)); /* sent to the client application. */
365
366 switch (pmsg->datakind) {
367 case 'R': strlcpy (datakind, "Version Number", sizeof(datakind)); break;
368 case 'X': strlcpy (datakind, "Callsign Registration", sizeof(datakind)); break;
369 case 'G': strlcpy (datakind, "Port Information", sizeof(datakind)); break;
370 case 'g': strlcpy (datakind, "Capabilities of a Port", sizeof(datakind)); break;
371 case 'y': strlcpy (datakind, "Frames Outstanding on a Port", sizeof(datakind)); break;
372 case 'Y': strlcpy (datakind, "Frames Outstanding on a Connection", sizeof(datakind)); break;
373 case 'H': strlcpy (datakind, "Heard Stations on a Port", sizeof(datakind)); break;
374 case 'C': strlcpy (datakind, "AX.25 Connection Received", sizeof(datakind)); break;
375 case 'D': strlcpy (datakind, "Connected AX.25 Data", sizeof(datakind)); break;
376 case 'd': strlcpy (datakind, "Disconnected", sizeof(datakind)); break;
377 case 'M': strlcpy (datakind, "Monitored Connected Information", sizeof(datakind)); break;
378 case 'S': strlcpy (datakind, "Monitored Supervisory Information", sizeof(datakind)); break;
379 case 'U': strlcpy (datakind, "Monitored Unproto Information", sizeof(datakind)); break;
380 case 'T': strlcpy (datakind, "Monitoring Own Information", sizeof(datakind)); break;
381 case 'K': strlcpy (datakind, "Monitored Information in Raw Format", sizeof(datakind)); break;
382 default: strlcpy (datakind, "**INVALID**", sizeof(datakind)); break;
383 }
384 }
385
386 text_color_set(DW_COLOR_DEBUG);
387 dw_printf ("\n");
388
389 dw_printf ("%s %s %s AGWPE client application %d, total length = %d\n",
390 prefix[(int)fromto], datakind, direction, client, msg_len);
391
392 dw_printf ("\tportx = %d, datakind = '%c', pid = 0x%02x\n", pmsg->portx, pmsg->datakind, pmsg->pid);
393 dw_printf ("\tcall_from = \"%s\", call_to = \"%s\"\n", pmsg->call_from, pmsg->call_to);
394 dw_printf ("\tdata_len = %d, user_reserved = %d, data =\n", netle2host(pmsg->data_len_NETLE), netle2host(pmsg->user_reserved_NETLE));
395
396 hex_dump ((unsigned char*)pmsg + sizeof(struct agwpe_s), netle2host(pmsg->data_len_NETLE));
397
398 if (msg_len < 36) {
399 text_color_set (DW_COLOR_ERROR);
400 dw_printf ("AGWPE message length, %d, is shorter than minumum 36.\n", msg_len);
401 }
402 if (msg_len != netle2host(pmsg->data_len_NETLE) + 36) {
403 text_color_set (DW_COLOR_ERROR);
404 dw_printf ("AGWPE message length, %d, inconsistent with data length %d.\n", msg_len, netle2host(pmsg->data_len_NETLE));
405 }
406
407 }
408
409 /*-------------------------------------------------------------------
410 *
411 * Name: server_init
412 *
413 * Purpose: Set up a server to listen for connection requests from
414 * an application such as Xastir.
415 *
416 * Inputs: mc->agwpe_port - TCP port for server.
417 * Main program has default of 8000 but allows
418 * an alternative to be specified on the command line
419 *
420 * 0 means disable. New in version 1.2.
421 *
422 * Outputs:
423 *
424 * Description: This starts at least two threads:
425 * * one to listen for a connection from client app.
426 * * one or more to listen for commands from client app.
427 * so the main application doesn't block while we wait for these.
428 *
429 *--------------------------------------------------------------------*/
430
431 static struct audio_s *save_audio_config_p;
432
433
server_init(struct audio_s * audio_config_p,struct misc_config_s * mc)434 void server_init (struct audio_s *audio_config_p, struct misc_config_s *mc)
435 {
436 int client;
437
438 #if __WIN32__
439 HANDLE connect_listen_th;
440 HANDLE cmd_listen_th[MAX_NET_CLIENTS];
441 #else
442 pthread_t connect_listen_tid;
443 pthread_t cmd_listen_tid[MAX_NET_CLIENTS];
444 int e;
445 #endif
446 int server_port = mc->agwpe_port; /* Usually 8000 but can be changed. */
447
448
449 #if DEBUG
450 text_color_set(DW_COLOR_DEBUG);
451 dw_printf ("server_init ( %d )\n", server_port);
452 debug_a = 1;
453 #endif
454
455 save_audio_config_p = audio_config_p;
456
457 for (client=0; client<MAX_NET_CLIENTS; client++) {
458 client_sock[client] = -1;
459 enable_send_raw_to_client[client] = 0;
460 enable_send_monitor_to_client[client] = 0;
461 }
462
463 if (server_port == 0) {
464 text_color_set(DW_COLOR_INFO);
465 dw_printf ("Disabled AGW network client port.\n");
466 return;
467 }
468
469
470 /*
471 * This waits for a client to connect and sets an available client_sock[n].
472 */
473 #if __WIN32__
474 connect_listen_th = (HANDLE)_beginthreadex (NULL, 0, connect_listen_thread, (void *)(ptrdiff_t)server_port, 0, NULL);
475 if (connect_listen_th == NULL) {
476 text_color_set(DW_COLOR_ERROR);
477 dw_printf ("Could not create AGW connect listening thread\n");
478 return;
479 }
480 #else
481 e = pthread_create (&connect_listen_tid, NULL, connect_listen_thread, (void *)(ptrdiff_t)server_port);
482 if (e != 0) {
483 text_color_set(DW_COLOR_ERROR);
484 perror("Could not create AGW connect listening thread");
485 return;
486 }
487 #endif
488
489 /*
490 * These read messages from client when client_sock[n] is valid.
491 * Currently we start up a separate thread for each potential connection.
492 * Possible later refinement. Start one now, others only as needed.
493 */
494 for (client = 0; client < MAX_NET_CLIENTS; client++) {
495
496 #if __WIN32__
497 cmd_listen_th[client] = (HANDLE)_beginthreadex (NULL, 0, cmd_listen_thread, (void*)(ptrdiff_t)client, 0, NULL);
498 if (cmd_listen_th[client] == NULL) {
499 text_color_set(DW_COLOR_ERROR);
500 dw_printf ("Could not create AGW command listening thread for client %d\n", client);
501 return;
502 }
503 #else
504 e = pthread_create (&cmd_listen_tid[client], NULL, cmd_listen_thread, (void *)(ptrdiff_t)client);
505 if (e != 0) {
506 text_color_set(DW_COLOR_ERROR);
507 dw_printf ("Could not create AGW command listening thread for client %d\n", client);
508 // Replace add perror with better message handling.
509 perror("");
510 return;
511 }
512 #endif
513 }
514 }
515
516
517 /*-------------------------------------------------------------------
518 *
519 * Name: connect_listen_thread
520 *
521 * Purpose: Wait for a connection request from an application.
522 *
523 * Inputs: arg - TCP port for server.
524 * Main program has default of 8000 but allows
525 * an alternative to be specified on the command line
526 *
527 * Outputs: client_sock - File descriptor for communicating with client app.
528 *
529 * Description: Wait for connection request from client and establish
530 * communication.
531 * Note that the client can go away and come back again and
532 * re-establish communication without restarting this application.
533 *
534 *--------------------------------------------------------------------*/
535
connect_listen_thread(void * arg)536 static THREAD_F connect_listen_thread (void *arg)
537 {
538 #if __WIN32__
539
540 struct addrinfo hints;
541 struct addrinfo *ai = NULL;
542 int err;
543 char server_port_str[12];
544
545 SOCKET listen_sock;
546 WSADATA wsadata;
547
548 snprintf (server_port_str, sizeof(server_port_str), "%d", (int)(ptrdiff_t)arg);
549 #if DEBUG
550 text_color_set(DW_COLOR_DEBUG);
551 dw_printf ("DEBUG: serverport = %d = '%s'\n", (int)(ptrdiff_t)arg, server_port_str);
552 #endif
553 err = WSAStartup (MAKEWORD(2,2), &wsadata);
554 if (err != 0) {
555 text_color_set(DW_COLOR_ERROR);
556 dw_printf("WSAStartup failed: %d\n", err);
557 return (0);
558 }
559
560 if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
561 text_color_set(DW_COLOR_ERROR);
562 dw_printf("Could not find a usable version of Winsock.dll\n");
563 WSACleanup();
564 //sleep (1);
565 return (0);
566 }
567
568 memset (&hints, 0, sizeof(hints));
569 hints.ai_family = AF_INET;
570 hints.ai_socktype = SOCK_STREAM;
571 hints.ai_protocol = IPPROTO_TCP;
572 hints.ai_flags = AI_PASSIVE;
573
574 err = getaddrinfo(NULL, server_port_str, &hints, &ai);
575 if (err != 0) {
576 text_color_set(DW_COLOR_ERROR);
577 dw_printf("getaddrinfo failed: %d\n", err);
578 //sleep (1);
579 WSACleanup();
580 return (0);
581 }
582
583 listen_sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
584 if (listen_sock == INVALID_SOCKET) {
585 text_color_set(DW_COLOR_ERROR);
586 dw_printf ("connect_listen_thread: Socket creation failed, err=%d", WSAGetLastError());
587 return (0);
588 }
589
590 #if DEBUG
591 text_color_set(DW_COLOR_DEBUG);
592 dw_printf("Binding to port %s ... \n", server_port_str);
593 #endif
594
595 err = bind( listen_sock, ai->ai_addr, (int)ai->ai_addrlen);
596 if (err == SOCKET_ERROR) {
597 text_color_set(DW_COLOR_ERROR);
598 dw_printf("Bind failed with error: %d\n", WSAGetLastError()); // TODO: translate number to text?
599 dw_printf("Some other application is probably already using port %s.\n", server_port_str);
600 dw_printf("Try using a different port number with AGWPORT in the configuration file.\n");
601 freeaddrinfo(ai);
602 closesocket(listen_sock);
603 WSACleanup();
604 return (0);
605 }
606
607 freeaddrinfo(ai);
608
609 #if DEBUG
610 text_color_set(DW_COLOR_DEBUG);
611 dw_printf("opened socket as fd (%d) on port (%s) for stream i/o\n", listen_sock, server_port_str );
612 #endif
613
614 while (1) {
615
616 int client;
617 int c;
618
619 client = -1;
620 for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) {
621 if (client_sock[c] <= 0) {
622 client = c;
623 }
624 }
625
626 /*
627 * Listen for connection if we have not reached maximum.
628 */
629 if (client >= 0) {
630
631 if(listen(listen_sock, MAX_NET_CLIENTS) == SOCKET_ERROR)
632 {
633 text_color_set(DW_COLOR_ERROR);
634 dw_printf("Listen failed with error: %d\n", WSAGetLastError());
635 return (0);
636 }
637
638 text_color_set(DW_COLOR_INFO);
639 dw_printf("Ready to accept AGW client application %d on port %s ...\n", client, server_port_str);
640
641 client_sock[client] = accept(listen_sock, NULL, NULL);
642
643 if (client_sock[client] == -1) {
644 text_color_set(DW_COLOR_ERROR);
645 dw_printf("Accept failed with error: %d\n", WSAGetLastError());
646 closesocket(listen_sock);
647 WSACleanup();
648 return (0);
649 }
650
651 text_color_set(DW_COLOR_INFO);
652 dw_printf("\nAttached to AGW client application %d ...\n\n", client);
653
654 /*
655 * The command to change this is actually a toggle, not explicit on or off.
656 * Make sure it has proper state when we get a new connection.
657 */
658 enable_send_raw_to_client[client] = 0;
659 enable_send_monitor_to_client[client] = 0;
660 }
661 else {
662 SLEEP_SEC(1); /* wait then check again if more clients allowed. */
663 }
664 }
665
666 #else /* End of Windows case, now Linux */
667
668
669 struct sockaddr_in sockaddr; /* Internet socket address stuct */
670 socklen_t sockaddr_size = sizeof(struct sockaddr_in);
671 int server_port = (int)(ptrdiff_t)arg;
672 int listen_sock;
673 int bcopt = 1;
674
675 listen_sock= socket(AF_INET,SOCK_STREAM,0);
676 if (listen_sock == -1) {
677 text_color_set(DW_COLOR_ERROR);
678 perror ("connect_listen_thread: Socket creation failed");
679 return (NULL);
680 }
681
682 /* Version 1.3 - as suggested by G8BPQ. */
683 /* Without this, if you kill the application then try to run it */
684 /* again quickly the port number is unavailable for a while. */
685 /* Don't try doing the same thing On Windows; It has a different meaning. */
686 /* http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t */
687
688 setsockopt (listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&bcopt, 4);
689
690
691 sockaddr.sin_addr.s_addr = INADDR_ANY;
692 sockaddr.sin_port = htons(server_port);
693 sockaddr.sin_family = AF_INET;
694
695 #if DEBUG
696 text_color_set(DW_COLOR_DEBUG);
697 dw_printf("Binding to port %d ... \n", server_port);
698 #endif
699
700 if (bind(listen_sock,(struct sockaddr*)&sockaddr,sizeof(sockaddr)) == -1) {
701 text_color_set(DW_COLOR_ERROR);
702 dw_printf("Bind failed with error: %d\n", errno);
703 dw_printf("%s\n", strerror(errno));
704 dw_printf("Some other application is probably already using port %d.\n", server_port);
705 dw_printf("Try using a different port number with AGWPORT in the configuration file.\n");
706 return (NULL);
707 }
708
709 getsockname( listen_sock, (struct sockaddr *)(&sockaddr), &sockaddr_size);
710
711 #if DEBUG
712 text_color_set(DW_COLOR_DEBUG);
713 dw_printf("opened socket as fd (%d) on port (%d) for stream i/o\n", listen_sock, ntohs(sockaddr.sin_port) );
714 #endif
715
716 while (1) {
717
718 int client;
719 int c;
720
721 client = -1;
722 for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) {
723 if (client_sock[c] <= 0) {
724 client = c;
725 }
726 }
727
728 if (client >= 0) {
729
730 if(listen(listen_sock,MAX_NET_CLIENTS) == -1)
731 {
732 text_color_set(DW_COLOR_ERROR);
733 perror ("connect_listen_thread: Listen failed");
734 return (NULL);
735 }
736
737 text_color_set(DW_COLOR_INFO);
738 dw_printf("Ready to accept AGW client application %d on port %d ...\n", client, server_port);
739
740 client_sock[client] = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size);
741
742 text_color_set(DW_COLOR_INFO);
743 dw_printf("\nAttached to AGW client application %d...\n\n", client);
744
745 /*
746 * The command to change this is actually a toggle, not explicit on or off.
747 * Make sure it has proper state when we get a new connection.
748 */
749 enable_send_raw_to_client[client] = 0;
750 enable_send_monitor_to_client[client] = 0;
751 }
752 else {
753 SLEEP_SEC(1); /* wait then check again if more clients allowed. */
754 }
755 }
756 #endif
757 }
758
759
760 /*-------------------------------------------------------------------
761 *
762 * Name: server_send_rec_packet
763 *
764 * Purpose: Send a received packet to the client app.
765 *
766 * Inputs: chan - Channel number where packet was received.
767 * 0 = first, 1 = second if any.
768 *
769 * pp - Identifier for packet object.
770 *
771 * fbuf - Address of raw received frame buffer.
772 * flen - Length of raw received frame.
773 *
774 *
775 * Description: Send message to client if connected.
776 * Disconnect from client, and notify user, if any error.
777 *
778 * There are two different formats:
779 * RAW - the original received frame.
780 * MONITOR - human readable monitoring format.
781 *
782 *--------------------------------------------------------------------*/
783
784 static void mon_addrs (int chan, packet_t pp, char *result, int result_size);
785 static char mon_desc (packet_t pp, char *result, int result_size);
786
787
server_send_rec_packet(int chan,packet_t pp,unsigned char * fbuf,int flen)788 void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int flen)
789 {
790 struct {
791 struct agwpe_s hdr;
792 char data[1+AX25_MAX_PACKET_LEN];
793 } agwpe_msg;
794
795 int err;
796
797 /*
798 * RAW format
799 */
800 for (int client=0; client<MAX_NET_CLIENTS; client++) {
801
802 if (enable_send_raw_to_client[client] && client_sock[client] > 0){
803
804 memset (&agwpe_msg.hdr, 0, sizeof(agwpe_msg.hdr));
805
806 agwpe_msg.hdr.portx = chan;
807
808 agwpe_msg.hdr.datakind = 'K';
809
810 ax25_get_addr_with_ssid (pp, AX25_SOURCE, agwpe_msg.hdr.call_from);
811
812 ax25_get_addr_with_ssid (pp, AX25_DESTINATION, agwpe_msg.hdr.call_to);
813
814 agwpe_msg.hdr.data_len_NETLE = host2netle(flen + 1);
815
816 /* Stick in extra byte for the "TNC" to use. */
817
818 agwpe_msg.data[0] = 0;
819 memcpy (agwpe_msg.data + 1, fbuf, (size_t)flen);
820
821 if (debug_client) {
822 debug_print (TO_CLIENT, client, &agwpe_msg.hdr, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
823 }
824
825 #if __WIN32__
826 err = SOCK_SEND (client_sock[client], (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
827 if (err == SOCKET_ERROR)
828 {
829 text_color_set(DW_COLOR_ERROR);
830 dw_printf ("\nError %d sending message to AGW client application. Closing connection.\n\n", WSAGetLastError());
831 closesocket (client_sock[client]);
832 client_sock[client] = -1;
833 WSACleanup();
834 dlq_client_cleanup (client);
835 }
836 #else
837 err = SOCK_SEND (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
838 if (err <= 0)
839 {
840 text_color_set(DW_COLOR_ERROR);
841 dw_printf ("\nError sending message to AGW client application. Closing connection.\n\n");
842 close (client_sock[client]);
843 client_sock[client] = -1;
844 dlq_client_cleanup (client);
845 }
846 #endif
847 }
848 }
849
850 // Application might want more human readable format.
851
852 server_send_monitored (chan, pp, 0);
853
854 } /* end server_send_rec_packet */
855
856
857
server_send_monitored(int chan,packet_t pp,int own_xmit)858 void server_send_monitored (int chan, packet_t pp, int own_xmit)
859 {
860 /*
861 * MONITOR format - 'I' for information frames.
862 * 'U' for unnumbered information.
863 * 'S' for supervisory and other unnumbered.
864 */
865 struct {
866 struct agwpe_s hdr;
867 char data[1+AX25_MAX_PACKET_LEN];
868 } agwpe_msg;
869
870 int err;
871
872 for (int client=0; client<MAX_NET_CLIENTS; client++) {
873
874 if (enable_send_monitor_to_client[client] && client_sock[client] > 0) {
875
876 memset (&agwpe_msg.hdr, 0, sizeof(agwpe_msg.hdr));
877
878 agwpe_msg.hdr.portx = chan; // datakind is added later.
879 ax25_get_addr_with_ssid (pp, AX25_SOURCE, agwpe_msg.hdr.call_from);
880 ax25_get_addr_with_ssid (pp, AX25_DESTINATION, agwpe_msg.hdr.call_to);
881
882 /* http://uz7ho.org.ua/includes/agwpeapi.htm#_Toc500723812 */
883
884 /* Description mentions one CR character after timestamp but example has two. */
885 /* Actual observed cases have only one. */
886 /* Also need to add extra CR, CR, null at end. */
887 /* The documentation example includes these 3 extra in the Len= value */
888 /* but actual observed data uses only the packet info length. */
889
890 // Documentation doesn't mention anything about including the via path.
891 // In version 1.4, we add that to match observed behaviour.
892
893 // This inconsistency was reported:
894 // Direwolf:
895 // [AGWE-IN] 1:Fm ZL4FOX-8 To Q7P2U2 [08:25:07]`I1*l V>/"9<}[:Barts Tracker 3.83V X
896 // AGWPE:
897 // [AGWE-IN] 1:Fm ZL4FOX-8 To Q7P2U2 Via WIDE3-3 [08:32:14]`I0*l V>/"98}[:Barts Tracker 3.83V X
898
899 // Format the channel and addresses, with leading and trailing space.
900
901 mon_addrs (chan, pp, (char*)(agwpe_msg.data), sizeof(agwpe_msg.data));
902
903 // Add the description with <... >
904
905 char desc[80];
906 agwpe_msg.hdr.datakind = mon_desc (pp, desc, sizeof(desc));
907 if (own_xmit) {
908 agwpe_msg.hdr.datakind = 'T';
909 }
910 strlcat ((char*)(agwpe_msg.data), desc, sizeof(agwpe_msg.data));
911
912 // Timestamp with [...]\r
913
914 time_t clock = time(NULL);
915 struct tm *tm = localtime(&clock); // TODO: use localtime_r ?
916 char ts[32];
917 snprintf (ts, sizeof(ts), "[%02d:%02d:%02d]\r", tm->tm_hour, tm->tm_min, tm->tm_sec);
918 strlcat ((char*)(agwpe_msg.data), ts, sizeof(agwpe_msg.data));
919
920 // Information if any with \r\r.
921
922 unsigned char *pinfo = NULL;
923 int info_len = ax25_get_info (pp, &pinfo);
924 if (info_len > 0 && pinfo != NULL) {
925 strlcat ((char*)(agwpe_msg.data), (char*)pinfo, sizeof(agwpe_msg.data));
926 strlcat ((char*)(agwpe_msg.data), "\r", sizeof(agwpe_msg.data));
927 }
928
929 agwpe_msg.hdr.data_len_NETLE = host2netle(strlen(agwpe_msg.data) + 1) /* +1 to include terminating null */ ;
930
931 if (debug_client) {
932 debug_print (TO_CLIENT, client, &agwpe_msg.hdr, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
933 }
934
935 #if __WIN32__
936 err = SOCK_SEND (client_sock[client], (char*)(&agwpe_msg), sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
937 if (err == SOCKET_ERROR)
938 {
939 text_color_set(DW_COLOR_ERROR);
940 dw_printf ("\nError %d sending message to AGW client application %d. Closing connection.\n\n", WSAGetLastError(), client);
941 closesocket (client_sock[client]);
942 client_sock[client] = -1;
943 WSACleanup();
944 dlq_client_cleanup (client);
945 }
946 #else
947 err = SOCK_SEND (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
948 if (err <= 0)
949 {
950 text_color_set(DW_COLOR_ERROR);
951 dw_printf ("\nError sending message to AGW client application %d. Closing connection.\n\n", client);
952 close (client_sock[client]);
953 client_sock[client] = -1;
954 dlq_client_cleanup (client);
955 }
956 #endif
957 }
958 }
959
960 } /* server_send_monitored */
961
962
963 // Next two are broken out in case they can be reused elsewhere.
964
965 // Format addresses in AGWPR monitoring format such as:
966 // 1:Fm ZL4FOX-8 To Q7P2U2 Via WIDE3-3
967
mon_addrs(int chan,packet_t pp,char * result,int result_size)968 static void mon_addrs (int chan, packet_t pp, char *result, int result_size)
969 {
970 char src[AX25_MAX_ADDR_LEN];
971 char dst[AX25_MAX_ADDR_LEN];
972
973 ax25_get_addr_with_ssid (pp, AX25_SOURCE, src);
974 ax25_get_addr_with_ssid (pp, AX25_DESTINATION, dst);
975 int num_digi = ax25_get_num_repeaters(pp);
976
977 if (num_digi > 0) {
978
979 char via[AX25_MAX_REPEATERS*(AX25_MAX_ADDR_LEN+1)];
980 char stemp[AX25_MAX_ADDR_LEN+1];
981 int j;
982
983 ax25_get_addr_with_ssid (pp, AX25_REPEATER_1, via);
984 for (j = 1; j < num_digi; j++) {
985 ax25_get_addr_with_ssid (pp, AX25_REPEATER_1 + j, stemp);
986 strlcat (via, ",", sizeof(via));
987 strlcat (via, stemp, sizeof(via));
988 }
989 snprintf (result, result_size, " %d:Fm %s To %s Via %s ",
990 chan+1, src, dst, via);
991 }
992 else {
993 snprintf (result, result_size, " %d:Fm %s To %s ",
994 chan+1, src, dst);
995 }
996 }
997
998
999 // Generate frame description in AGWPE monitoring format such as
1000 // <UI pid=F0 Len=123 >
1001 // <I R1 S3 pid=F0 Len=123 >
1002 // <RR P1 R5 >
1003 //
1004 // Returns:
1005 // 'I' for information frame.
1006 // 'U' for unnumbered information frame.
1007 // 'S' for supervisory and other unnumbered frames.
1008
mon_desc(packet_t pp,char * result,int result_size)1009 static char mon_desc (packet_t pp, char *result, int result_size)
1010 {
1011 cmdres_t cr; // command/response.
1012 char ignore[80]; // direwolf description. not used here.
1013 int pf; // poll/final bit.
1014 int ns; // N(S) Send sequence number.
1015 int nr; // N(R) Received sequence number.
1016 char pf_text[4]; // P or F depending on whether command or response.
1017
1018 ax25_frame_type_t ftype = ax25_frame_type (pp, &cr, ignore, &pf, &nr, &ns);
1019
1020 switch (cr) {
1021 case cr_cmd: strcpy(pf_text, "P"); break; // P only: I, SABME, SABM, DISC
1022 case cr_res: strcpy(pf_text, "F"); break; // F only: DM, UA, FRMR
1023 // Either: RR, RNR, REJ, SREJ, UI, XID, TEST
1024
1025 default: strcpy(pf_text, "PF"); break; // Not AX.25 version >= 2.0
1026 // APRS is often sloppy about this but it
1027 // is essential for connected mode.
1028 }
1029
1030 unsigned char *pinfo = NULL; // I, UI, XID, SREJ, TEST can have information part.
1031 int info_len = ax25_get_info (pp, &pinfo);
1032
1033 switch (ftype) {
1034
1035 case frame_type_I: snprintf (result, result_size, "<I S%d R%d pid=0x%02X Len=%d %s=%d >", ns, nr, ax25_get_pid(pp), info_len, pf_text, pf); return ('I');
1036
1037 case frame_type_U_UI: snprintf (result, result_size, "<UI pid=%02X Len=%d %s=%d >", ax25_get_pid(pp), info_len, pf_text, pf); return ('U'); break;
1038
1039 case frame_type_S_RR: snprintf (result, result_size, "<RR R%d %s=%d >", nr, pf_text, pf); return ('S'); break;
1040 case frame_type_S_RNR: snprintf (result, result_size, "<RNR R%d %s=%d >", nr, pf_text, pf); return ('S'); break;
1041 case frame_type_S_REJ: snprintf (result, result_size, "<REJ R%d %s=%d >", nr, pf_text, pf); return ('S'); break;
1042 case frame_type_S_SREJ: snprintf (result, result_size, "<SREJ R%d %s=%d Len=%d >", nr, pf_text, pf, info_len); return ('S'); break;
1043
1044 case frame_type_U_SABME: snprintf (result, result_size, "<SABME %s=%d >", pf_text, pf); return ('S'); break;
1045 case frame_type_U_SABM: snprintf (result, result_size, "<SABM %s=%d >", pf_text, pf); return ('S'); break;
1046 case frame_type_U_DISC: snprintf (result, result_size, "<DISC %s=%d >", pf_text, pf); return ('S'); break;
1047 case frame_type_U_DM: snprintf (result, result_size, "<DM %s=%d >", pf_text, pf); return ('S'); break;
1048 case frame_type_U_UA: snprintf (result, result_size, "<UA %s=%d >", pf_text, pf); return ('S'); break;
1049 case frame_type_U_FRMR: snprintf (result, result_size, "<FRMR %s=%d >", pf_text, pf); return ('S'); break;
1050 case frame_type_U_XID: snprintf (result, result_size, "<XID %s=%d Len=%d >", pf_text, pf, info_len); return ('S'); break;
1051 case frame_type_U_TEST: snprintf (result, result_size, "<TEST %s=%d Len=%d >", pf_text, pf, info_len); return ('S'); break;
1052 default:
1053 case frame_type_U: snprintf (result, result_size, "<U other??? >"); return ('S'); break;
1054 }
1055 }
1056
1057
1058 /*-------------------------------------------------------------------
1059 *
1060 * Name: server_link_established
1061 *
1062 * Purpose: Send notification to client app when a link has
1063 * been established with another station.
1064 *
1065 * DL-CONNECT Confirm or DL-CONNECT Indication in the protocol spec.
1066 *
1067 * Inputs: chan - Which radio channel.
1068 *
1069 * client - Which one of potentially several clients.
1070 *
1071 * remote_call - Callsign[-ssid] of remote station.
1072 *
1073 * own_call - Callsign[-ssid] of my end.
1074 *
1075 * incoming - true if connection was initiated from other end.
1076 * false if this end started it.
1077 *
1078 *--------------------------------------------------------------------*/
1079
server_link_established(int chan,int client,char * remote_call,char * own_call,int incoming)1080 void server_link_established (int chan, int client, char *remote_call, char *own_call, int incoming)
1081 {
1082
1083 struct {
1084 struct agwpe_s hdr;
1085 char info[100];
1086 } reply;
1087
1088
1089 memset (&reply, 0, sizeof(reply));
1090 reply.hdr.portx = chan;
1091 reply.hdr.datakind = 'C';
1092
1093 strlcpy (reply.hdr.call_from, remote_call, sizeof(reply.hdr.call_from));
1094 strlcpy (reply.hdr.call_to, own_call, sizeof(reply.hdr.call_to));
1095
1096 // Question: Should the via path be provided too?
1097
1098 if (incoming) {
1099 // Other end initiated the connection.
1100 snprintf (reply.info, sizeof(reply.info), "*** CONNECTED To Station %s\r", remote_call);
1101 }
1102 else {
1103 // We started the connection.
1104 snprintf (reply.info, sizeof(reply.info), "*** CONNECTED With Station %s\r", remote_call);
1105 }
1106 reply.hdr.data_len_NETLE = host2netle(strlen(reply.info) + 1);
1107
1108 send_to_client (client, &reply);
1109
1110 } /* end server_link_established */
1111
1112
1113
1114 /*-------------------------------------------------------------------
1115 *
1116 * Name: server_link_terminated
1117 *
1118 * Purpose: Send notification to client app when a link with
1119 * another station has been terminated or a connection
1120 * attempt failed.
1121 *
1122 * DL-DISCONNECT Confirm or DL-DISCONNECT Indication in the protocol spec.
1123 *
1124 * Inputs: chan - Which radio channel.
1125 *
1126 * client - Which one of potentially several clients.
1127 *
1128 * remote_call - Callsign[-ssid] of remote station.
1129 *
1130 * own_call - Callsign[-ssid] of my end.
1131 *
1132 * timeout - true when no answer from other station.
1133 * How do we distinguish who asked for the
1134 * termination of an existing link?
1135 *
1136 *--------------------------------------------------------------------*/
1137
server_link_terminated(int chan,int client,char * remote_call,char * own_call,int timeout)1138 void server_link_terminated (int chan, int client, char *remote_call, char *own_call, int timeout)
1139 {
1140
1141 struct {
1142 struct agwpe_s hdr;
1143 char info[100];
1144 } reply;
1145
1146
1147 memset (&reply, 0, sizeof(reply));
1148 reply.hdr.portx = chan;
1149 reply.hdr.datakind = 'd';
1150 strlcpy (reply.hdr.call_from, remote_call, sizeof(reply.hdr.call_from)); /* right order */
1151 strlcpy (reply.hdr.call_to, own_call, sizeof(reply.hdr.call_to));
1152
1153 if (timeout) {
1154 snprintf (reply.info, sizeof(reply.info), "*** DISCONNECTED RETRYOUT With %s\r", remote_call);
1155 }
1156 else {
1157 snprintf (reply.info, sizeof(reply.info), "*** DISCONNECTED From Station %s\r", remote_call);
1158 }
1159 reply.hdr.data_len_NETLE = host2netle(strlen(reply.info) + 1);
1160
1161 send_to_client (client, &reply);
1162
1163
1164 } /* end server_link_terminated */
1165
1166
1167 /*-------------------------------------------------------------------
1168 *
1169 * Name: server_rec_conn_data
1170 *
1171 * Purpose: Send received connected data to the application.
1172 *
1173 * DL-DATA Indication in the protocol spec.
1174 *
1175 * Inputs: chan - Which radio channel.
1176 *
1177 * client - Which one of potentially several clients.
1178 *
1179 * remote_call - Callsign[-ssid] of remote station.
1180 *
1181 * own_call - Callsign[-ssid] of my end.
1182 *
1183 * pid - Protocol ID from I frame.
1184 *
1185 * data_ptr - Pointer to a block of bytes.
1186 *
1187 * data_len - Number of bytes. Could be zero.
1188 *
1189 *--------------------------------------------------------------------*/
1190
server_rec_conn_data(int chan,int client,char * remote_call,char * own_call,int pid,char * data_ptr,int data_len)1191 void server_rec_conn_data (int chan, int client, char *remote_call, char *own_call, int pid, char *data_ptr, int data_len)
1192 {
1193
1194 struct {
1195 struct agwpe_s hdr;
1196 char info[AX25_MAX_INFO_LEN]; // I suppose there is potential for something larger.
1197 // We'll cross that bridge if we ever come to it.
1198 } reply;
1199
1200
1201 memset (&reply.hdr, 0, sizeof(reply.hdr));
1202 reply.hdr.portx = chan;
1203 reply.hdr.datakind = 'D';
1204 reply.hdr.pid = pid;
1205
1206 strlcpy (reply.hdr.call_from, remote_call, sizeof(reply.hdr.call_from));
1207 strlcpy (reply.hdr.call_to, own_call, sizeof(reply.hdr.call_to));
1208
1209 if (data_len < 0) {
1210 text_color_set(DW_COLOR_ERROR);
1211 dw_printf ("Invalid length %d for connected data to client %d.\n", data_len, client);
1212 data_len = 0;
1213 }
1214 else if (data_len > AX25_MAX_INFO_LEN) {
1215 text_color_set(DW_COLOR_ERROR);
1216 dw_printf ("Invalid length %d for connected data to client %d.\n", data_len, client);
1217 data_len = AX25_MAX_INFO_LEN;
1218 }
1219
1220 memcpy (reply.info, data_ptr, data_len);
1221 reply.hdr.data_len_NETLE = host2netle(data_len);
1222
1223 send_to_client (client, &reply);
1224
1225 } /* end server_rec_conn_data */
1226
1227
1228 /*-------------------------------------------------------------------
1229 *
1230 * Name: server_outstanding_frames_reply
1231 *
1232 * Purpose: Send 'Y' Outstanding frames for connected data to the application.
1233 *
1234 * Inputs: chan - Which radio channel.
1235 *
1236 * client - Which one of potentially several clients.
1237 *
1238 * own_call - Callsign[-ssid] of my end.
1239 *
1240 * remote_call - Callsign[-ssid] of remote station.
1241 *
1242 * count - Number of frames sent from the application but
1243 * not yet received by the other station.
1244 *
1245 *--------------------------------------------------------------------*/
1246
server_outstanding_frames_reply(int chan,int client,char * own_call,char * remote_call,int count)1247 void server_outstanding_frames_reply (int chan, int client, char *own_call, char *remote_call, int count)
1248 {
1249
1250 struct {
1251 struct agwpe_s hdr;
1252 int count_NETLE;
1253 } reply;
1254
1255
1256 memset (&reply.hdr, 0, sizeof(reply.hdr));
1257
1258 reply.hdr.portx = chan;
1259 reply.hdr.datakind = 'Y';
1260
1261 strlcpy (reply.hdr.call_from, own_call, sizeof(reply.hdr.call_from));
1262 strlcpy (reply.hdr.call_to, remote_call, sizeof(reply.hdr.call_to));
1263
1264 reply.hdr.data_len_NETLE = host2netle(4);
1265 reply.count_NETLE = host2netle(count);
1266
1267 send_to_client (client, &reply);
1268
1269 } /* end server_outstanding_frames_reply */
1270
1271
1272 /*-------------------------------------------------------------------
1273 *
1274 * Name: read_from_socket
1275 *
1276 * Purpose: Read from socket until we have desired number of bytes.
1277 *
1278 * Inputs: fd - file descriptor.
1279 * ptr - address where data should be placed.
1280 * len - desired number of bytes.
1281 *
1282 * Description: Just a wrapper for the "read" system call but it should
1283 * never return fewer than the desired number of bytes.
1284 *
1285 *--------------------------------------------------------------------*/
1286
read_from_socket(int fd,char * ptr,int len)1287 static int read_from_socket (int fd, char *ptr, int len)
1288 {
1289 int got_bytes = 0;
1290
1291 #if DEBUG
1292 text_color_set(DW_COLOR_DEBUG);
1293 dw_printf ("read_from_socket (%d, %p, %d)\n", fd, ptr, len);
1294 #endif
1295 while (got_bytes < len) {
1296 int n;
1297
1298 n = SOCK_RECV (fd, ptr + got_bytes, len - got_bytes);
1299
1300 #if DEBUG
1301 text_color_set(DW_COLOR_DEBUG);
1302 dw_printf ("read_from_socket: n = %d\n", n);
1303 #endif
1304 if (n <= 0) {
1305 return (n);
1306 }
1307
1308 got_bytes += n;
1309 }
1310 assert (got_bytes >= 0 && got_bytes <= len);
1311
1312 #if DEBUG
1313 text_color_set(DW_COLOR_DEBUG);
1314 dw_printf ("read_from_socket: return %d\n", got_bytes);
1315 #endif
1316 return (got_bytes);
1317 }
1318
1319
1320 /*-------------------------------------------------------------------
1321 *
1322 * Name: cmd_listen_thread
1323 *
1324 * Purpose: Wait for command messages from an application.
1325 *
1326 * Inputs: arg - client number, 0 .. MAX_NET_CLIENTS-1
1327 *
1328 * Outputs: client_sock[n] - File descriptor for communicating with client app.
1329 *
1330 * Description: Process messages from the client application.
1331 * Note that the client can go away and come back again and
1332 * re-establish communication without restarting this application.
1333 *
1334 *--------------------------------------------------------------------*/
1335
1336
send_to_client(int client,void * reply_p)1337 static void send_to_client (int client, void *reply_p)
1338 {
1339 struct agwpe_s *ph;
1340 int len;
1341 int err;
1342
1343 ph = (struct agwpe_s *) reply_p; // Replies are often hdr + other stuff.
1344
1345 len = sizeof(struct agwpe_s) + netle2host(ph->data_len_NETLE);
1346
1347 /* Not sure what max data length might be. */
1348
1349 if (netle2host(ph->data_len_NETLE) < 0 || netle2host(ph->data_len_NETLE) > 4096) {
1350 text_color_set(DW_COLOR_ERROR);
1351 dw_printf ("Invalid data length %d for AGW protocol message to client %d.\n", netle2host(ph->data_len_NETLE), client);
1352 debug_print (TO_CLIENT, client, ph, len);
1353 }
1354
1355 if (debug_client) {
1356 debug_print (TO_CLIENT, client, ph, len);
1357 }
1358
1359 err = SOCK_SEND (client_sock[client], (char*)(ph), len);
1360 (void)err;
1361 }
1362
1363
cmd_listen_thread(void * arg)1364 static THREAD_F cmd_listen_thread (void *arg)
1365 {
1366 int n;
1367
1368 struct {
1369 struct agwpe_s hdr; /* Command header. */
1370
1371 char data[AX25_MAX_PACKET_LEN]; /* Additional data used by some commands. */
1372 /* Maximum for 'V': 1 + 8*10 + 256 */
1373 /* Maximum for 'D': Info part length + 1 */
1374 } cmd;
1375
1376 int client = (int)(ptrdiff_t)arg;
1377
1378 assert (client >= 0 && client < MAX_NET_CLIENTS);
1379
1380 while (1) {
1381
1382 while (client_sock[client] <= 0) {
1383 SLEEP_SEC(1); /* Not connected. Try again later. */
1384 }
1385
1386 n = read_from_socket (client_sock[client], (char *)(&cmd.hdr), sizeof(cmd.hdr));
1387 if (n != sizeof(cmd.hdr)) {
1388 text_color_set(DW_COLOR_ERROR);
1389 dw_printf ("\nError getting message header from AGW client application %d.\n", client);
1390 dw_printf ("Tried to read %d bytes but got only %d.\n", (int)sizeof(cmd.hdr), n);
1391 dw_printf ("Closing connection.\n\n");
1392 #if __WIN32__
1393 closesocket (client_sock[client]);
1394 #else
1395 close (client_sock[client]);
1396 #endif
1397 client_sock[client] = -1;
1398 dlq_client_cleanup (client);
1399 continue;
1400 }
1401
1402 /*
1403 * Take some precautions to guard against bad data which could cause problems later.
1404 */
1405 if (cmd.hdr.portx < 0 || cmd.hdr.portx >= MAX_CHANS) {
1406 text_color_set(DW_COLOR_ERROR);
1407 dw_printf ("\nInvalid port number, %d, in command '%c', from AGW client application %d.\n",
1408 cmd.hdr.portx, cmd.hdr.datakind, client);
1409 cmd.hdr.portx = 0; // avoid subscript out of bounds, try to keep going.
1410 }
1411
1412 /*
1413 * Call to/from fields are 10 bytes but contents must not exceeed 9 characters.
1414 * It's not guaranteed that unused bytes will contain 0 so we
1415 * don't issue error message in this case.
1416 */
1417 cmd.hdr.call_from[sizeof(cmd.hdr.call_from)-1] = '\0';
1418 cmd.hdr.call_to[sizeof(cmd.hdr.call_to)-1] = '\0';
1419
1420 /*
1421 * Following data must fit in available buffer.
1422 * Leave room for an extra nul byte terminator at end later.
1423 */
1424
1425 int data_len = netle2host(cmd.hdr.data_len_NETLE);
1426
1427 if (data_len < 0 || data_len > (int)(sizeof(cmd.data) - 1)) {
1428
1429 text_color_set(DW_COLOR_ERROR);
1430 dw_printf ("\nInvalid message from AGW client application %d.\n", client);
1431 dw_printf ("Data Length of %d is out of range.\n", data_len);
1432
1433 /* This is a bad situation. */
1434 /* If we tried to read again, the header probably won't be there. */
1435 /* No point in trying to continue reading. */
1436
1437 dw_printf ("Closing connection.\n\n");
1438 #if __WIN32__
1439 closesocket (client_sock[client]);
1440 #else
1441 close (client_sock[client]);
1442 #endif
1443 client_sock[client] = -1;
1444 dlq_client_cleanup (client);
1445 return (0);
1446 }
1447
1448 cmd.data[0] = '\0';
1449
1450 if (data_len > 0) {
1451 n = read_from_socket (client_sock[client], cmd.data, data_len);
1452 if (n != data_len) {
1453 text_color_set(DW_COLOR_ERROR);
1454 dw_printf ("\nError getting message data from AGW client application %d.\n", client);
1455 dw_printf ("Tried to read %d bytes but got only %d.\n", data_len, n);
1456 dw_printf ("Closing connection.\n\n");
1457 #if __WIN32__
1458 closesocket (client_sock[client]);
1459 #else
1460 close (client_sock[client]);
1461 #endif
1462 client_sock[client] = -1;
1463 dlq_client_cleanup (client);
1464 return (0);
1465 }
1466 if (n >= 0) {
1467 cmd.data[n] = '\0'; // Tidy if we print for debug.
1468 }
1469 }
1470
1471 /*
1472 * print & process message from client.
1473 */
1474
1475 if (debug_client) {
1476 debug_print (FROM_CLIENT, client, &cmd.hdr, sizeof(cmd.hdr) + data_len);
1477 }
1478
1479 switch (cmd.hdr.datakind) {
1480
1481 case 'R': /* Request for version number */
1482 {
1483 struct {
1484 struct agwpe_s hdr;
1485 int major_version_NETLE;
1486 int minor_version_NETLE;
1487 } reply;
1488
1489
1490 memset (&reply, 0, sizeof(reply));
1491 reply.hdr.datakind = 'R';
1492 reply.hdr.data_len_NETLE = host2netle(sizeof(reply.major_version_NETLE) + sizeof(reply.minor_version_NETLE));
1493 assert (netle2host(reply.hdr.data_len_NETLE) == 8);
1494
1495 // Xastir only prints this and doesn't care otherwise.
1496 // APRSIS32 doesn't seem to care.
1497 // UI-View32 wants on 2000.15 or later.
1498
1499 reply.major_version_NETLE = host2netle(2005);
1500 reply.minor_version_NETLE = host2netle(127);
1501
1502 assert (sizeof(reply) == 44);
1503
1504 send_to_client (client, &reply);
1505
1506 }
1507 break;
1508
1509 case 'G': /* Ask about radio ports */
1510
1511 {
1512 struct {
1513 struct agwpe_s hdr;
1514 char info[200];
1515 } reply;
1516
1517
1518 int j, count;
1519
1520
1521 memset (&reply, 0, sizeof(reply));
1522 reply.hdr.datakind = 'G';
1523
1524
1525 // Xastir only prints this and doesn't care otherwise.
1526 // YAAC uses this to identify available channels.
1527
1528 // The interface manual wants the first to be "Port1"
1529 // so channel 0 corresponds to "Port1."
1530 // We can have gaps in the numbering.
1531 // I wonder what applications will think about that.
1532
1533 // No other place cares about total number.
1534
1535 count = 0;
1536 for (j=0; j<MAX_CHANS; j++) {
1537 if (save_audio_config_p->achan[j].medium == MEDIUM_RADIO ||
1538 save_audio_config_p->achan[j].medium == MEDIUM_IGATE ||
1539 save_audio_config_p->achan[j].medium == MEDIUM_NETTNC) {
1540 count++;
1541 }
1542 }
1543 snprintf (reply.info, sizeof(reply.info), "%d;", count);
1544
1545 for (j=0; j<MAX_CHANS; j++) {
1546
1547 switch (save_audio_config_p->achan[j].medium) {
1548
1549 case MEDIUM_RADIO:
1550 {
1551 char stemp[100];
1552 int a = ACHAN2ADEV(j);
1553 // If I was really ambitious, some description could be provided.
1554 static const char *names[8] = { "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth" };
1555
1556 if (save_audio_config_p->adev[a].num_channels == 1) {
1557 snprintf (stemp, sizeof(stemp), "Port%d %s soundcard mono;", j+1, names[a]);
1558 strlcat (reply.info, stemp, sizeof(reply.info));
1559 }
1560 else {
1561 snprintf (stemp, sizeof(stemp), "Port%d %s soundcard %s;", j+1, names[a], j&1 ? "right" : "left");
1562 strlcat (reply.info, stemp, sizeof(reply.info));
1563 }
1564 }
1565 break;
1566
1567 case MEDIUM_IGATE:
1568 {
1569 char stemp[100];
1570 snprintf (stemp, sizeof(stemp), "Port%d Internet Gateway;", j+1);
1571 strlcat (reply.info, stemp, sizeof(reply.info));
1572 }
1573 break;
1574
1575 case MEDIUM_NETTNC:
1576 {
1577 // could elaborate with hostname, etc.
1578 char stemp[100];
1579 snprintf (stemp, sizeof(stemp), "Port%d Network TNC;", j+1);
1580 strlcat (reply.info, stemp, sizeof(reply.info));
1581 }
1582 break;
1583
1584 default:
1585 {
1586 // could elaborate with hostname, etc.
1587 char stemp[100];
1588 snprintf (stemp, sizeof(stemp), "Port%d INVALID CHANNEL;", j+1);
1589 strlcat (reply.info, stemp, sizeof(reply.info));
1590 }
1591 break;
1592
1593 } // switch
1594 } // for each channel
1595
1596 reply.hdr.data_len_NETLE = host2netle(strlen(reply.info) + 1);
1597
1598 send_to_client (client, &reply);
1599 }
1600 break;
1601
1602
1603 case 'g': /* Ask about capabilities of a port. */
1604
1605 {
1606 struct {
1607 struct agwpe_s hdr;
1608 unsigned char on_air_baud_rate; /* 0=1200, 1=2400, 2=4800, 3=9600, ... */
1609 unsigned char traffic_level; /* 0xff if not in autoupdate mode */
1610 unsigned char tx_delay;
1611 unsigned char tx_tail;
1612 unsigned char persist;
1613 unsigned char slottime;
1614 unsigned char maxframe;
1615 unsigned char active_connections;
1616 int how_many_bytes_NETLE;
1617 } reply;
1618
1619
1620 memset (&reply, 0, sizeof(reply));
1621
1622 reply.hdr.portx = cmd.hdr.portx; /* Reply with same port number ! */
1623 reply.hdr.datakind = 'g';
1624 reply.hdr.data_len_NETLE = host2netle(12);
1625
1626 // YAAC asks for this.
1627 // Fake it to keep application happy.
1628 // TODO: Supply real values instead of just faking it.
1629
1630 reply.on_air_baud_rate = 0;
1631 reply.traffic_level = 1;
1632 reply.tx_delay = 0x19;
1633 reply.tx_tail = 4;
1634 reply.persist = 0xc8;
1635 reply.slottime = 4;
1636 reply.maxframe = 7;
1637 reply.active_connections = 0;
1638 reply.how_many_bytes_NETLE = host2netle(1);
1639
1640 assert (sizeof(reply) == 48);
1641
1642 send_to_client (client, &reply);
1643
1644 }
1645 break;
1646
1647
1648 case 'H': /* Ask about recently heard stations on given port. */
1649
1650 /* This should send back 20 'H' frames for the most recently heard stations. */
1651 /* If there are less available, empty frames are sent to make a total of 20. */
1652 /* Each contains the first and last heard times. */
1653
1654 {
1655 #if 0 /* Currently, this information is not being collected. */
1656 struct {
1657 struct agwpe_s hdr;
1658 char info[100];
1659 } reply;
1660
1661
1662 memset (&reply.hdr, 0, sizeof(reply.hdr));
1663 reply.hdr.datakind = 'H';
1664
1665 // TODO: Implement properly.
1666
1667 reply.hdr.portx = cmd.hdr.portx
1668
1669 strlcpy (reply.hdr.call_from, "WB2OSZ-15 Mon,01Jan2000 01:02:03 Tue,31Dec2099 23:45:56", sizeof(reply.hdr.call_from));
1670 // or 00:00:00 00:00:00
1671
1672 strlcpy (agwpe_msg.data, ..., sizeof(agwpe_msg.data));
1673
1674 reply.hdr.data_len_NETLE = host2netle(strlen(reply.info));
1675
1676 send_to_client (client, &reply);
1677 #endif
1678 }
1679 break;
1680
1681
1682
1683
1684 case 'k': /* Ask to start receiving RAW AX25 frames */
1685
1686 // Actually it is a toggle so we must be sure to clear it for a new connection.
1687
1688 enable_send_raw_to_client[client] = ! enable_send_raw_to_client[client];
1689 break;
1690
1691 case 'm': /* Ask to start receiving Monitor frames */
1692
1693 // Actually it is a toggle so we must be sure to clear it for a new connection.
1694
1695 enable_send_monitor_to_client[client] = ! enable_send_monitor_to_client[client];
1696 break;
1697
1698
1699 case 'V': /* Transmit UI data frame (with digipeater path) */
1700 {
1701 // Data format is:
1702 // 1 byte for number of digipeaters.
1703 // 10 bytes for each digipeater.
1704 // data part of message.
1705
1706 char stemp[AX25_MAX_PACKET_LEN+2];
1707 char *p;
1708 int ndigi;
1709 int k;
1710
1711 packet_t pp;
1712
1713 strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp));
1714 strlcat (stemp, ">", sizeof(stemp));
1715 strlcat (stemp, cmd.hdr.call_to, sizeof(stemp));
1716
1717 cmd.data[data_len] = '\0';
1718 ndigi = cmd.data[0];
1719 p = cmd.data + 1;
1720
1721 for (k=0; k<ndigi; k++) {
1722 strlcat (stemp, ",", sizeof(stemp));
1723 strlcat (stemp, p, sizeof(stemp));
1724 p += 10;
1725 }
1726 strlcat (stemp, ":", sizeof(stemp));
1727 strlcat (stemp, p, sizeof(stemp));
1728
1729 //text_color_set(DW_COLOR_DEBUG);
1730 //dw_printf ("Transmit '%s'\n", stemp);
1731
1732 pp = ax25_from_text (stemp, 1);
1733
1734
1735 if (pp == NULL) {
1736 text_color_set(DW_COLOR_ERROR);
1737 dw_printf ("Failed to create frame from AGW 'V' message.\n");
1738 }
1739 else {
1740
1741 /* This goes into the low priority queue because it is an original. */
1742
1743 /* Note that the protocol has no way to set the "has been used" */
1744 /* bits in the digipeater fields. */
1745
1746 /* This explains why the digipeating option is grayed out in */
1747 /* xastir when using the AGW interface. */
1748 /* The current version uses only the 'V' message, not 'K' for transmitting. */
1749
1750 tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
1751
1752 }
1753 }
1754
1755 break;
1756
1757 case 'K': /* Transmit raw AX.25 frame */
1758 {
1759 // Message contains:
1760 // port number for transmission.
1761 // data length
1762 // data which is raw ax.25 frame.
1763 //
1764
1765 packet_t pp;
1766 alevel_t alevel;
1767
1768 // Bug fix in version 1.1:
1769 //
1770 // The first byte of data is described as:
1771 //
1772 // the "TNC" to use
1773 // 00=Port 1
1774 // 16=Port 2
1775 //
1776 // I don't know what that means; we already a port number in the header.
1777 // Anyhow, the original code here added one to cmd.data to get the
1778 // first byte of the frame. Unfortunately, it did not subtract one from
1779 // cmd.hdr.data_len so we ended up sending an extra byte.
1780
1781 memset (&alevel, 0xff, sizeof(alevel));
1782 pp = ax25_from_frame ((unsigned char *)cmd.data+1, data_len - 1, alevel);
1783
1784 if (pp == NULL) {
1785 text_color_set(DW_COLOR_ERROR);
1786 dw_printf ("Failed to create frame from AGW 'K' message.\n");
1787 }
1788 else {
1789
1790 /* How can we determine if it is an original or repeated message? */
1791 /* If there is at least one digipeater in the frame, AND */
1792 /* that digipeater has been used, it should go out quickly thru */
1793 /* the high priority queue. */
1794 /* Otherwise, it is an original for the low priority queue. */
1795
1796 if (ax25_get_num_repeaters(pp) >= 1 &&
1797 ax25_get_h(pp,AX25_REPEATER_1)) {
1798 tq_append (cmd.hdr.portx, TQ_PRIO_0_HI, pp);
1799 }
1800 else {
1801 tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
1802 }
1803 }
1804 }
1805
1806 break;
1807
1808 case 'X': /* Register CallSign */
1809
1810 {
1811 struct {
1812 struct agwpe_s hdr;
1813 char data; /* 1 = success, 0 = failure */
1814 } reply;
1815
1816 int ok = 1;
1817
1818 // The protocol spec says it is an error to register the same one more than once.
1819 // Too much trouble. Report success if the channel is valid.
1820
1821
1822 int chan = cmd.hdr.portx;
1823
1824 // Connected mode can only be used with internal modems.
1825
1826 if (chan >= 0 && chan < MAX_CHANS && save_audio_config_p->achan[chan].medium == MEDIUM_RADIO) {
1827 ok = 1;
1828 dlq_register_callsign (cmd.hdr.call_from, chan, client);
1829 }
1830 else {
1831 text_color_set(DW_COLOR_ERROR);
1832 dw_printf ("AGW protocol error. Register callsign for invalid channel %d.\n", chan);
1833 ok = 0;
1834 }
1835
1836
1837 memset (&reply, 0, sizeof(reply));
1838 reply.hdr.datakind = 'X';
1839 reply.hdr.portx = cmd.hdr.portx;
1840 memcpy (reply.hdr.call_from, cmd.hdr.call_from, sizeof(reply.hdr.call_from));
1841 reply.hdr.data_len_NETLE = host2netle(1);
1842 reply.data = ok;
1843 send_to_client (client, &reply);
1844 }
1845 break;
1846
1847 case 'x': /* Unregister CallSign */
1848
1849 {
1850
1851 int chan = cmd.hdr.portx;
1852
1853 // Connected mode can only be used with internal modems.
1854
1855 if (chan >= 0 && chan < MAX_CHANS && save_audio_config_p->achan[chan].medium == MEDIUM_RADIO) {
1856 dlq_unregister_callsign (cmd.hdr.call_from, chan, client);
1857 }
1858 else {
1859 text_color_set(DW_COLOR_ERROR);
1860 dw_printf ("AGW protocol error. Unregister callsign for invalid channel %d.\n", chan);
1861 }
1862 }
1863 /* No reponse is expected. */
1864 break;
1865
1866 case 'C': /* Connect, Start an AX.25 Connection */
1867 case 'v': /* Connect VIA, Start an AX.25 circuit thru digipeaters */
1868 case 'c': /* Connection with non-standard PID */
1869
1870 {
1871 struct via_info {
1872 unsigned char num_digi; /* Expect to be in range 1 to 7. Why not up to 8? */
1873 char dcall[7][10];
1874 }
1875 #if 1
1876 // October 2017. gcc ??? complained:
1877 // warning: dereferencing pointer 'v' does break strict-aliasing rules
1878 // Try adding this attribute to get rid of the warning.
1879 // If this upsets your compiler, take it out.
1880 // Let me know. Maybe we could put in a compiler version check here.
1881
1882 __attribute__((__may_alias__))
1883 #endif
1884 *v = (struct via_info *)cmd.data;
1885
1886 char callsigns[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
1887 int num_calls = 2; /* 2 plus any digipeaters. */
1888 int pid = 0xf0; /* normal for AX.25 I frames. */
1889 int j;
1890
1891 strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
1892 strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_DESTINATION]));
1893
1894 if (cmd.hdr.datakind == 'c') {
1895 pid = cmd.hdr.pid; /* non standard for NETROM, TCP/IP, etc. */
1896 }
1897
1898 if (cmd.hdr.datakind == 'v') {
1899 if (v->num_digi >= 1 && v->num_digi <= 7) {
1900
1901 if (data_len != v->num_digi * 10 + 1 && data_len != v->num_digi * 10 + 2) {
1902 // I'm getting 1 more than expected from AGWterminal.
1903 text_color_set(DW_COLOR_ERROR);
1904 dw_printf ("AGW client, connect via, has data len, %d when %d expected.\n", data_len, v->num_digi * 10 + 1);
1905 }
1906
1907 for (j = 0; j < v->num_digi; j++) {
1908 strlcpy (callsigns[AX25_REPEATER_1 + j], v->dcall[j], sizeof(callsigns[AX25_REPEATER_1 + j]));
1909 num_calls++;
1910 }
1911 }
1912 else {
1913 text_color_set(DW_COLOR_ERROR);
1914 dw_printf ("\n");
1915 dw_printf ("AGW client, connect via, has invalid number of digipeaters = %d\n", v->num_digi);
1916 }
1917 }
1918
1919
1920 dlq_connect_request (callsigns, num_calls, cmd.hdr.portx, client, pid);
1921
1922 }
1923 break;
1924
1925
1926 case 'D': /* Send Connected Data */
1927
1928 {
1929 char callsigns[2][AX25_MAX_ADDR_LEN];
1930 const int num_calls = 2;
1931
1932 strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
1933 strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
1934
1935 dlq_xmit_data_request (callsigns, num_calls, cmd.hdr.portx, client, cmd.hdr.pid, cmd.data, netle2host(cmd.hdr.data_len_NETLE));
1936
1937 }
1938 break;
1939
1940 case 'd': /* Disconnect, Terminate an AX.25 Connection */
1941
1942 {
1943 char callsigns[2][AX25_MAX_ADDR_LEN];
1944 const int num_calls = 2;
1945
1946 strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
1947 strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
1948
1949 dlq_disconnect_request (callsigns, num_calls, cmd.hdr.portx, client);
1950
1951 }
1952 break;
1953
1954
1955 case 'M': /* Send UNPROTO Information (no digipeater path) */
1956
1957 /*
1958 Added in version 1.3.
1959 This is the same as 'V' except there is no provision for digipeaters.
1960 TODO: combine 'V' and 'M' into one case.
1961 AGWterminal sends this for beacon or ask QRA.
1962
1963 <<< Send UNPROTO Information from AGWPE client application 0, total length = 253
1964 portx = 0, datakind = 'M', pid = 0x00
1965 call_from = "WB2OSZ-15", call_to = "BEACON"
1966 data_len = 217, user_reserved = 556, data =
1967 000: 54 68 69 73 20 76 65 72 73 69 6f 6e 20 75 73 65 This version use
1968 ...
1969
1970 <<< Send UNPROTO Information from AGWPE client application 0, total length = 37
1971 portx = 0, datakind = 'M', pid = 0x00
1972 call_from = "WB2OSZ-15", call_to = "QRA"
1973 data_len = 1, user_reserved = 31759424, data =
1974 000: 0d .
1975 .
1976
1977 There is also a report of it coming from UISS.
1978
1979 <<< Send UNPROTO Information from AGWPE client application 0, total length = 50
1980 portx = 0, port_hi_reserved = 0
1981 datakind = 77 = 'M', kind_hi = 0
1982 call_from = "JH4XSY", call_to = "APRS"
1983 data_len = 14, user_reserved = 0, data =
1984 000: 21 22 3c 43 2e 74 71 6c 48 72 71 21 21 5f !"<C.tqlHrq!!_
1985 */
1986 {
1987
1988 int pid = cmd.hdr.pid;
1989 (void)(pid);
1990 /* The AGW protocol spec says, */
1991 /* "AX.25 PID 0x00 or 0xF0 for AX.25 0xCF NETROM and others" */
1992
1993 /* BUG: In theory, the AX.25 PID octet should be set from this. */
1994 /* All examples seen (above) have 0. */
1995 /* The AX.25 protocol spec doesn't list 0 as a valid value. */
1996 /* We always send 0xf0, meaning no layer 3. */
1997 /* Maybe we should have an ax25_set_pid function for cases when */
1998 /* it is neither 0 nor 0xf0. */
1999
2000 char stemp[AX25_MAX_PACKET_LEN];
2001 packet_t pp;
2002
2003 strlcpy (stemp, cmd.hdr.call_from, sizeof(stemp));
2004 strlcat (stemp, ">", sizeof(stemp));
2005 strlcat (stemp, cmd.hdr.call_to, sizeof(stemp));
2006
2007 cmd.data[data_len] = '\0';
2008
2009 strlcat (stemp, ":", sizeof(stemp));
2010 strlcat (stemp, cmd.data, sizeof(stemp));
2011
2012 //text_color_set(DW_COLOR_DEBUG);
2013 //dw_printf ("Transmit '%s'\n", stemp);
2014
2015 pp = ax25_from_text (stemp, 1);
2016
2017 if (pp == NULL) {
2018 text_color_set(DW_COLOR_ERROR);
2019 dw_printf ("Failed to create frame from AGW 'M' message.\n");
2020 }
2021 else {
2022 tq_append (cmd.hdr.portx, TQ_PRIO_1_LO, pp);
2023 }
2024 }
2025 break;
2026
2027
2028 case 'y': /* Ask Outstanding frames waiting on a Port */
2029
2030 /* Number of frames sitting in transmit queue for specified channel. */
2031 {
2032 struct {
2033 struct agwpe_s hdr;
2034 int data_NETLE; // Little endian order.
2035 } reply;
2036
2037
2038 memset (&reply, 0, sizeof(reply));
2039 reply.hdr.portx = cmd.hdr.portx; /* Reply with same port number */
2040 reply.hdr.datakind = 'y';
2041 reply.hdr.data_len_NETLE = host2netle(4);
2042
2043 int n = 0;
2044 if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_CHANS) {
2045 // Count both normal and expedited in transmit queue for given channel.
2046 n = tq_count (cmd.hdr.portx, -1, "", "", 0);
2047 }
2048 reply.data_NETLE = host2netle(n);
2049
2050 send_to_client (client, &reply);
2051 }
2052 break;
2053
2054 case 'Y': /* How Many Outstanding frames wait for tx for a particular station */
2055
2056 // This is different than the above 'y' because this refers to a specific
2057 // link in connected mode.
2058
2059 // This would be useful for a couple different purposes.
2060
2061 // When sending bulk data, we want to keep a fair amount queued up to take
2062 // advantage of large window sizes (MAXFRAME, EMAXFRAME). On the other
2063 // hand we don't want to get TOO far ahead when transferring a large file.
2064
2065 // Before disconnecting from another station, it would be good to know
2066 // that it actually received the last message we sent. For this reason,
2067 // I think it would be good for this to include information frames that were
2068 // transmitted but not yet acknowleged.
2069 // You could say that a particular frame is still waiting to be sent even
2070 // if was already sent because it could be sent again if lost previously.
2071
2072 // The documentation is inconsistent about the address order.
2073 // One place says "callfrom" is my callsign and "callto" is the other guy.
2074 // That would make sense. We are asking about frames going to the other guy.
2075
2076 // But another place says it depends on who initiated the connection.
2077 //
2078 // "If we started the connection CallFrom=US and CallTo=THEM
2079 // If the other end started the connection CallFrom=THEM and CallTo=US"
2080 //
2081 // The response description says nothing about the order; it just mentions two addresses.
2082 // If you are writing a client or server application, the order would
2083 // be clear but right here it could be either case.
2084 //
2085 // Another version of the documentation mentioned the source address being optional.
2086 //
2087
2088 // The only way to get this information is from inside the data link state machine.
2089 // We will send a request to it and the result coming out will be used to
2090 // send the reply back to the client application.
2091
2092 {
2093
2094 char callsigns[2][AX25_MAX_ADDR_LEN];
2095 const int num_calls = 2;
2096
2097 strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
2098 strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
2099
2100 // Issue 169. Proper implementation for 'Y'.
2101 dlq_outstanding_frames_request (callsigns, num_calls, cmd.hdr.portx, client);
2102
2103 }
2104 break;
2105
2106 default:
2107
2108 text_color_set(DW_COLOR_ERROR);
2109 dw_printf ("--- Unexpected Command from application %d using AGW protocol:\n", client);
2110 debug_print (FROM_CLIENT, client, &cmd.hdr, sizeof(cmd.hdr) + data_len);
2111
2112 break;
2113 }
2114 }
2115
2116 } /* end send_to_client */
2117
2118
2119 /* end server.c */
2120