2017-09-12 01:53:38 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2017 John Langner, WB2OSZ
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 2 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Module: sock.c
|
|
|
|
*
|
|
|
|
* Purpose: Functions for TCP sockets.
|
|
|
|
*
|
|
|
|
* Description: These are used for connecting between different applications,
|
|
|
|
* possibly on different hosts.
|
|
|
|
*
|
|
|
|
* New in version 1.5:
|
|
|
|
* Duplicate code already exists in multiple places and I was about
|
|
|
|
* to add another one. Instead, we will gather the common code here
|
|
|
|
* instead of having yet another copy.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
|
|
|
|
#include <winsock2.h>
|
|
|
|
#include <ws2tcpip.h> // _WIN32_WINNT must be set to 0x0501 before including this
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
//#include <termios.h>
|
2018-11-23 21:33:45 +00:00
|
|
|
#include <errno.h>
|
2017-09-12 01:53:38 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include "textcolor.h"
|
|
|
|
#include "sock.h"
|
|
|
|
|
|
|
|
static void shuffle (struct addrinfo *host[], int nhosts);
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: sock_init
|
|
|
|
*
|
|
|
|
* Purpose: Preparation before using socket interface.
|
|
|
|
*
|
|
|
|
* Inputs: none
|
|
|
|
*
|
|
|
|
* Returns: 0 for success, -1 for error.
|
|
|
|
*
|
|
|
|
* Errors: Message is printed. I've never seen it fail.
|
|
|
|
*
|
|
|
|
* Description: Doesn't do anything for Linux.
|
|
|
|
*
|
|
|
|
* TODO: Use this instead of own copy in aclients.c
|
|
|
|
* TODO: Use this instead of own copy in appserver.c
|
|
|
|
* TODO: Use this instead of own copy in audio_win.c
|
|
|
|
* TODO: Use this instead of own copy in igate.c
|
|
|
|
* TODO: Use this instead of own copy in kissnet.c
|
|
|
|
* TODO: Use this instead of own copy in kissutil.c
|
|
|
|
* TODO: Use this instead of own copy in server.c
|
|
|
|
* TODO: Use this instead of own copy in tnctest.c
|
|
|
|
* TODO: Use this instead of own copy in ttcalc.c
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
int sock_init(void)
|
|
|
|
{
|
|
|
|
#if __WIN32__
|
|
|
|
WSADATA wsadata;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = WSAStartup (MAKEWORD(2,2), &wsadata);
|
|
|
|
if (err != 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf("WSAStartup failed, error: %d\n", err);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf("Could not find a usable version of Winsock.dll\n");
|
|
|
|
WSACleanup();
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
} /* end sock_init */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: sock_connect
|
|
|
|
*
|
|
|
|
* Purpose: Connect to given host / port.
|
|
|
|
*
|
|
|
|
* Inputs: hostname - Host name or IP address.
|
|
|
|
*
|
|
|
|
* port - TCP port as text string.
|
|
|
|
*
|
|
|
|
* description - Description of the remote server to be used in error message.
|
|
|
|
* e.g. "APRS-IS (Igate) Server" or "TCP KISS TNC".
|
|
|
|
*
|
|
|
|
* allow_ipv6 - True to allow IPv6. Otherwise only IPv4.
|
|
|
|
*
|
|
|
|
* debug - Print debugging information.
|
|
|
|
*
|
|
|
|
* Outputs: ipaddr_str - The IP address, in text form, is placed here in case
|
|
|
|
* the caller wants it. Should be SOCK_IPADDR_LEN bytes.
|
|
|
|
*
|
|
|
|
* Returns: Socket Handle / file descriptor or -1 for error.
|
|
|
|
*
|
|
|
|
* Errors: (1) Can't find address for given host name.
|
|
|
|
*
|
|
|
|
* Print error and return -1.
|
|
|
|
*
|
|
|
|
* (2) Can't connect to one of the address(es).
|
|
|
|
*
|
|
|
|
* Silently try the next one.
|
|
|
|
*
|
|
|
|
* (3) Can't connect to any of the address(es).
|
|
|
|
*
|
|
|
|
* Nothing is printed for success. The caller might do that
|
|
|
|
* to provide confirmation on what is happening.
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
int sock_connect (char *hostname, char *port, char *description, int allow_ipv6, int debug, char ipaddr_str[SOCK_IPADDR_LEN])
|
|
|
|
{
|
|
|
|
#define MAX_HOSTS 50
|
|
|
|
|
|
|
|
struct addrinfo hints;
|
|
|
|
struct addrinfo *ai_head = NULL;
|
|
|
|
struct addrinfo *ai;
|
|
|
|
struct addrinfo *hosts[MAX_HOSTS];
|
|
|
|
int num_hosts, n;
|
|
|
|
int err;
|
|
|
|
int server_sock = -1;
|
|
|
|
|
|
|
|
strlcpy (ipaddr_str, "???", SOCK_IPADDR_LEN);
|
|
|
|
memset (&hints, 0, sizeof(hints));
|
|
|
|
|
|
|
|
hints.ai_family = AF_UNSPEC; /* Allow either IPv4 or IPv6. */
|
|
|
|
if ( ! allow_ipv6) {
|
|
|
|
hints.ai_family = AF_INET; /* IPv4 only. */
|
|
|
|
}
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First, we need to look up the DNS name to get IP address.
|
|
|
|
* It is possible to have multiple addresses.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ai_head = NULL;
|
|
|
|
err = getaddrinfo(hostname, port, &hints, &ai_head);
|
|
|
|
if (err != 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
#if __WIN32__
|
|
|
|
dw_printf ("Can't get address for %s, %s, err=%d\n",
|
|
|
|
description, hostname, WSAGetLastError());
|
|
|
|
#else
|
|
|
|
dw_printf ("Can't get address for %s, %s, %s\n",
|
|
|
|
description, hostname, gai_strerror(err));
|
|
|
|
#endif
|
|
|
|
freeaddrinfo(ai_head);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug) {
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
dw_printf ("getaddrinfo returns:\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
num_hosts = 0;
|
|
|
|
for (ai = ai_head; ai != NULL; ai = ai->ai_next) {
|
|
|
|
|
|
|
|
if (debug) {
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
sock_ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, SOCK_IPADDR_LEN);
|
|
|
|
dw_printf (" %s\n", ipaddr_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
hosts[num_hosts] = ai;
|
|
|
|
if (num_hosts < MAX_HOSTS) num_hosts++;
|
|
|
|
}
|
|
|
|
|
|
|
|
shuffle (hosts, num_hosts);
|
|
|
|
|
|
|
|
if (debug) {
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
dw_printf ("addresses for hostname:\n");
|
|
|
|
for (n=0; n<num_hosts; n++) {
|
|
|
|
sock_ia_to_text (hosts[n]->ai_family, hosts[n]->ai_addr, ipaddr_str, SOCK_IPADDR_LEN);
|
|
|
|
dw_printf (" %s\n", ipaddr_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try each address until we find one that is successful.
|
|
|
|
*/
|
|
|
|
for (n = 0; n < num_hosts; n++) {
|
|
|
|
#if __WIN32__
|
|
|
|
SOCKET is;
|
|
|
|
#else
|
|
|
|
int is;
|
|
|
|
#endif
|
|
|
|
ai = hosts[n];
|
|
|
|
|
|
|
|
sock_ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, SOCK_IPADDR_LEN);
|
|
|
|
is = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
|
|
|
#if __WIN32__
|
|
|
|
if (is == INVALID_SOCKET) {
|
|
|
|
printf ("Socket creation failed, err=%d", WSAGetLastError());
|
|
|
|
WSACleanup();
|
|
|
|
is = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (err != 0) {
|
|
|
|
printf ("Socket creation failed, err=%s", gai_strerror(err));
|
|
|
|
(void) close (is);
|
|
|
|
is = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef DEBUG_DNS
|
|
|
|
err = connect(is, ai->ai_addr, (int)ai->ai_addrlen);
|
|
|
|
#if __WIN32__
|
|
|
|
if (err == SOCKET_ERROR) {
|
|
|
|
#if DEBUGx
|
|
|
|
printf("Connect to %s on %s (%s), port %s failed.\n",
|
|
|
|
description, hostname, ipaddr_str, port);
|
|
|
|
#endif
|
|
|
|
closesocket (is);
|
|
|
|
is = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (err != 0) {
|
|
|
|
#if DEBUGx
|
|
|
|
printf("Connect to %s on %s (%s), port %s failed.\n",
|
|
|
|
description, hostname, ipaddr_str, port);
|
|
|
|
#endif
|
|
|
|
(void) close (is);
|
|
|
|
is = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IGate documentation says to use no delay. */
|
|
|
|
/* Does it really make a difference? */
|
|
|
|
int flag = 1;
|
|
|
|
err = setsockopt (is, IPPROTO_TCP, TCP_NODELAY, (void*)(long)(&flag), (socklen_t)sizeof(flag));
|
|
|
|
if (err < 0) {
|
|
|
|
printf("setsockopt TCP_NODELAY failed.\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Success. */
|
|
|
|
|
|
|
|
|
|
|
|
server_sock = is;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(ai_head);
|
|
|
|
|
|
|
|
// no, caller should handle this.
|
|
|
|
// function should be generally be silent unless debug option.
|
|
|
|
|
|
|
|
if (server_sock == -1) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf("Unable to connect to %s at %s (%s), port %s\n",
|
|
|
|
description, hostname, ipaddr_str, port );
|
|
|
|
}
|
|
|
|
|
|
|
|
return (server_sock);
|
|
|
|
|
|
|
|
} /* end sock_connect */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: sock_bind
|
|
|
|
*
|
|
|
|
* Purpose: We also have a bunch of duplicate code for the server side.
|
|
|
|
*
|
|
|
|
* Inputs:
|
|
|
|
*
|
|
|
|
* TODO: Use this instead of own copy in audio.c
|
|
|
|
* TODO: Use this instead of own copy in audio_portaudio.c
|
|
|
|
* TODO: Use this instead of own copy in audio_win.c
|
|
|
|
* TODO: Use this instead of own copy in kissnet.c
|
|
|
|
* TODO: Use this instead of own copy in server.c
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
// Not implemented yet.
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Addresses don't get mixed up very well.
|
|
|
|
* IPv6 always shows up last so we'd probably never
|
|
|
|
* end up using any of them for APRS-IS server.
|
|
|
|
* Add our own shuffle.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void shuffle (struct addrinfo *host[], int nhosts)
|
|
|
|
{
|
|
|
|
int j, k;
|
|
|
|
|
|
|
|
assert (RAND_MAX >= nhosts); /* for % to work right */
|
|
|
|
|
|
|
|
if (nhosts < 2) return;
|
|
|
|
|
|
|
|
srand (time(NULL));
|
|
|
|
|
|
|
|
for (j=0; j<nhosts; j++) {
|
|
|
|
k = rand() % nhosts;
|
|
|
|
assert (k >=0 && k<nhosts);
|
|
|
|
if (j != k) {
|
|
|
|
struct addrinfo *temp;
|
|
|
|
temp = host[j]; host[j] = host[k]; host[k] = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: sock_ia_to_text
|
|
|
|
*
|
|
|
|
* Purpose: Convert binary IP Address to text form.
|
|
|
|
*
|
|
|
|
* Inputs: Family - AF_INET or AF_INET6.
|
|
|
|
*
|
|
|
|
* pAddr - Pointer to the IP Address storage location.
|
|
|
|
*
|
|
|
|
* StringBufSize - Number of bytes in pStringBuf.
|
|
|
|
*
|
|
|
|
* Outputs: pStringBuf - Text result is placed here.
|
|
|
|
*
|
|
|
|
* Returns: pStringBuf
|
|
|
|
*
|
|
|
|
* Description: Can't use InetNtop because it is supported only on Windows Vista and later.
|
|
|
|
* At one time Dire Wolf worked on Win XP. Haven't tried it for years.
|
|
|
|
* Maybe some other dependency on a newer OS version has crept in.
|
|
|
|
*
|
|
|
|
* TODO: Use this instead of own copy in aclients.c
|
|
|
|
* TODO: Use this instead of own copy in appserver.c
|
|
|
|
* TODO: Use this instead of own copy in igate.c
|
|
|
|
* TODO: Use this instead of own copy in tnctest.c
|
|
|
|
* TODO: Use this instead of own copy in ttcalc.c
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
char *sock_ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t StringBufSize)
|
|
|
|
{
|
|
|
|
struct sockaddr_in *sa4;
|
|
|
|
struct sockaddr_in6 *sa6;
|
|
|
|
|
|
|
|
switch (Family) {
|
|
|
|
|
|
|
|
case AF_INET:
|
|
|
|
sa4 = (struct sockaddr_in *)pAddr;
|
|
|
|
#if __WIN32__
|
|
|
|
snprintf (pStringBuf, StringBufSize, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
|
|
|
|
sa4->sin_addr.S_un.S_un_b.s_b2,
|
|
|
|
sa4->sin_addr.S_un.S_un_b.s_b3,
|
|
|
|
sa4->sin_addr.S_un.S_un_b.s_b4);
|
|
|
|
#else
|
|
|
|
inet_ntop (AF_INET, &(sa4->sin_addr), pStringBuf, StringBufSize);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AF_INET6:
|
|
|
|
sa6 = (struct sockaddr_in6 *)pAddr;
|
|
|
|
#if __WIN32__
|
|
|
|
snprintf (pStringBuf, StringBufSize, "%x:%x:%x:%x:%x:%x:%x:%x",
|
|
|
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]),
|
|
|
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
|
|
|
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]),
|
|
|
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[3]),
|
|
|
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[4]),
|
|
|
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[5]),
|
|
|
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[6]),
|
|
|
|
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[7]));
|
|
|
|
#else
|
|
|
|
inet_ntop (AF_INET6, &(sa6->sin6_addr), pStringBuf, StringBufSize);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
snprintf (pStringBuf, StringBufSize, "Invalid address family!");
|
|
|
|
}
|
|
|
|
return pStringBuf;
|
|
|
|
|
|
|
|
} /* end sock_ia_to_text */
|
|
|
|
|
|
|
|
/* end sock.c */
|