1 /*
2  *
3  * XASTIR, Amateur Station Tracking and Information Reporting
4  * Copyright (C) 2000-2019 The Xastir Group
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * Look at the README for more information on the program.
21  */
22 
23 
24 
25 #ifdef HAVE_CONFIG_H
26   #include "config.h"
27 #endif  // HAVE_CONFIG_H
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <setjmp.h>
33 #include <sys/socket.h>
34 #include <string.h>
35 
36 #include <netinet/in.h>     // Moved ahead of inet.h as reports of some *BSD's not
37 // including this as they should.
38 #include <arpa/inet.h>
39 //#include <netinet/tcp.h>    // Needed for TCP_NODELAY setsockopt() (disabling Nagle algorithm)
40 
41 #ifdef HAVE_NETDB_H
42   #include <netdb.h>
43 #endif  // HAVE_NETDB_H
44 
45 #include <sys/types.h>
46 #include <sys/wait.h>
47 #include <errno.h>
48 
49 #include <poll.h>
50 
51 #include "xastir.h"
52 
53 // Must be last include file
54 #include "leak_detection.h"
55 
56 // Atttempt to send to one of the addresses, waiting for 10 seconds
57 // for (hopefully) a response. Returns 1 on success or 0 if we didn't
58 // get a response. (Any response is considered a success)
try_exchange(struct addrinfo * addr,char * buffer,int UNUSED (buflen))59 int try_exchange(struct addrinfo *addr, char *buffer, int UNUSED(buflen) )
60 {
61   int sockfd, n;
62   socklen_t length;
63   struct sockaddr_storage from;
64   struct pollfd polls;
65 
66   sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
67   if (sockfd < 0)
68   {
69     fprintf(stderr, "socket error: %s\n", strerror(errno));
70     return(0);
71   }
72 
73   n = sendto(sockfd, buffer, (size_t)strlen(buffer), 0, addr->ai_addr,
74              addr->ai_addrlen);
75   if (n < 0)
76   {
77     fprintf(stderr, "Sendto error %s\n", strerror(errno));
78     close(sockfd);
79     return(0);
80   }
81 
82   polls.fd = sockfd;
83   polls.events = POLLIN;
84 
85   // wait for up to 10 seconds for a response.
86   n = poll(&polls, 1, 10 * 1000);
87   if(n == 0)
88   {
89     fprintf(stderr, "Timeout waiting for response\n");
90     close(sockfd);
91     return 0;
92   }
93   else if (n < 0 )
94   {
95     fprintf(stderr, "poll() returned an error: %s\n", strerror(errno));
96     close(sockfd);
97     return 0;
98   }
99 
100   // Response should be waiting, get it.
101   length = sizeof(from);
102   n = recvfrom(sockfd, buffer, 256, 0, (struct sockaddr *)&from, &length);
103   if (n < 0)
104   {
105     fprintf(stderr, "recvfrom: %s\n", strerror(errno));
106     close(sockfd);
107     return(0);
108   }
109 
110   close(sockfd);
111   return 1;
112 }
113 
114 #ifndef AI_DEFAULT
115   #define AI_DEFAULT (AI_V4MAPPED|AI_ADDRCONFIG)
116 #endif
117 
118 // Loop through the possible addresses for hostname (probably IPv6 and IPv4)
119 // Tries until we are successful (get a reponse) or we run out of addresses
exchange_packet(char * hostname,char * port,char * buffer,int buflen)120 int exchange_packet(char *hostname, char *port, char *buffer, int buflen)
121 {
122   struct addrinfo hints, *res, *r;
123   int error;
124   int success = 0;
125 
126   memset(&hints, 0, sizeof(hints));
127   hints.ai_family = PF_UNSPEC;
128   hints.ai_socktype = SOCK_DGRAM;
129   hints.ai_flags = AI_DEFAULT;
130 
131   error = getaddrinfo(hostname, port, &hints, &res);
132   if (error)
133   {
134     fprintf(stderr, "Error: Unable to lookup addresses for host %s port %s\n",
135             hostname, port);
136     return 1;
137   }
138 
139   r = res;
140   while(!success && r)
141   {
142     success = try_exchange(r, buffer, buflen);
143     r = r->ai_next;
144     if(!success && r)
145     {
146       fprintf(stderr, "Trying next address to send to\n");
147     }
148   }
149 
150   freeaddrinfo(res);
151   return success;
152 }
153 
154 // Send a UDP packet to a UDP listening port.  This allows scripts
155 // and other programs to inject packets into Xastir via UDP
156 // protocol.
157 // Inputs:
158 //      hostname    (argv[1])
159 //      port        (argv[2])
160 //      callsign    (argv[3])
161 //      passcode    (argv[4])
162 //      optional flags:  -identify
163 //                       -to_rf
164 //                       -to_inet
165 //      APRS Packet (argv[5])
166 // Returns:
167 //      0: Message sent, ack received
168 //      1: Error condition
169 //
170 //
171 //
main(int argc,char * argv[])172 int main(int argc, char *argv[])
173 {
174   char buffer[512];
175   char callsign[10];
176   char extra[100];
177   int passcode;
178   char message[256];
179   int ii, success;
180 
181 
182   if (argc < 6)
183   {
184     fprintf(stderr,
185             "\nUsage: xastir_udp_client server port call passcode -identify\n");
186     fprintf(stderr,
187             "       xastir_udp_client server port call passcode [-to_rf] [-to_inet] \"APRS Packet\"\n");
188 
189     fprintf(stderr,
190             "\nExample: xastir_udp_client localhost 2023 ab7cd 1234 \"APRS packet goes here\"\n");
191     return(1);
192   }
193 
194   // Fetch the callsign
195   snprintf(callsign, sizeof(callsign), "%s", argv[3]);
196   callsign[sizeof(callsign)-1] = '\0';  // Terminate it
197 
198   // Fetch the passcode
199   passcode = atoi(argv[4]);
200 
201   // Check for optional flags here:
202   //      -identify
203   //      -to_rf
204   //      -to_inet
205   //
206   extra[0] = '\0';
207   for (ii = 5; ii < argc; ii++)
208   {
209     if (strstr(argv[ii], "-identify"))
210     {
211 //fprintf(stderr,"Found -identify\n");
212       strncat(extra, ",-identify", sizeof(extra)-strlen(extra)-1);
213     }
214     else if (strstr(argv[ii], "-to_rf"))
215     {
216 //fprintf(stderr,"Found -to_rf\n");
217       strncat(extra, ",-to_rf", sizeof(extra)-strlen(extra)-1);
218     }
219     else if (strstr(argv[ii], "-to_inet"))
220     {
221 //fprintf(stderr,"Found -to_inet\n");
222       strncat(extra, ",-to_inet", sizeof(extra)-strlen(extra)-1);
223     }
224   }
225 
226 
227 //    fprintf(stdout, "Please enter the message: ");
228 
229   // Fetch message portion from the end of the command-line
230   snprintf(message, sizeof(message), "%s", argv[argc-1]);
231   message[sizeof(message)-1] = '\0';  // Terminate it
232 
233   if (message[0] == '\0') // Empty message
234   {
235     return(1);
236   }
237 
238   memset(buffer, 0, 256);
239 //    fgets(buffer, 255, stdin);
240 
241   snprintf(buffer,
242            sizeof(buffer),
243            "%s,%d%s\n%s\n",
244            callsign,
245            passcode,
246            extra,
247            message);
248 
249 //fprintf(stderr, "%s", buffer);
250 
251   success = exchange_packet(argv[1], argv[2], buffer, 256);
252   if(!success)
253   {
254     fprintf(stdout, "No response received.\n");
255     return(1);
256   }
257   fprintf(stdout,"Received: %s\n", buffer);
258 
259   if (strncmp(buffer, "NACK", 4) == 0)
260   {
261 //fprintf(stderr,"returning 1\n");
262     return(1);  // Received a NACK
263   }
264   else if (strncmp(buffer, "ACK", 3) == 0)
265   {
266 //fprintf(stderr,"returning 0\n");
267     return(0);  // Received an ACK
268   }
269   else
270   {
271 //fprintf(stderr,"returning 1\n");
272     return(1);  // Received something other than ACK or NACK
273   }
274 }
275 
276 
277