Logo Search packages:      
Sourcecode: xcall version File versions  Download package

packet.c

/*  
 *  xcall - Packet radio program for GTK+
 *  Copyright (C) 2001 Joop Stakenborg <pg4i@amsat.org>
 *
 *  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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* packet.c: packet input and output functions */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <gtk/gtk.h>

#ifdef HAVE_NETAX25_AX25_H
#include <netax25/ax25.h>
#else
#include <netax25/kernel_ax25.h>
#endif
#ifdef HAVE_NETROM_NETROM_H
#include <netrom/netrom.h>
#else
#include <netax25/kernel_netrom.h>
#endif
#ifdef HAVE_NETROSE_ROSE_H
#include <netrose/rose.h>
#else
#include <netax25/kernel_rose.h>
#endif

#include <netax25/axlib.h>
#include <netax25/axconfig.h>
#include <netax25/nrconfig.h>
#include <netax25/rsconfig.h>

#include "init.h"
#include "packet.h"
#include "support.h"
#include "utils.h"
#include "types.h"

static gint s, addrlen = 0;
static gint axports = 0, nrports = 0, rsports = 0;
static gint rxmonitor = 0, connectmonitor = 0;
static gint mode = 0, af_mode = 0;
static gint connectpipe[2];
static gchar *port;
static gchar **list;
static union {
    struct full_sockaddr_ax25 ax25;
    struct sockaddr_rose rose;
  } sockaddr;
gboolean connected, connecting;
gchar *remotecallsign;
gchar buffer[256];
gint connectpid;
extern preferencestype preferences;
extern gboolean recording;
extern gint fprec;
GList *axportnames = NULL, *nrportnames = NULL, *rsportnames = NULL;
GList *connecthistory = NULL;

/************************ send and receive routines *************************/
static void rx(gpointer data, gint source, GdkInputCondition condition) {
  gchar rxbuf[8192], **rxsplit = NULL, *tmpstr;
  gint numbytes;

  memset(rxbuf, 0, 8192*sizeof(gchar));
  numbytes = read(s, rxbuf, 4096);
  if (numbytes <= 0) { 
    disconn();
    return;
  }
  (void *)g_strdelimit(rxbuf, "\r", '\n');

  if (preferences.dxcluster && (g_strncasecmp(rxbuf, "dx de ", 6) == 0)) {
    rxsplit = g_strsplit(rxbuf, ":", 2);
    g_strstrip(rxsplit[1]);
    tmpstr = g_strdup(rxsplit[1]);
    rxsplit = g_strsplit(tmpstr, " ", 2);
    if (strlen(rxsplit[0]) == 6) {
      if (g_strncasecmp(rxsplit[0], "1", 1) == 0)
        write_text(rxbuf, DX1_8MESSAGE);
      else if (g_strncasecmp(rxsplit[0], "3", 1) == 0)
        write_text(rxbuf, DX3_5MESSAGE);
      else if (g_strncasecmp(rxsplit[0], "7", 1) == 0)
        write_text(rxbuf, DX7MESSAGE);
      else write_text(rxbuf, DXOTHERMESSAGE);
    }
    else if (strlen(rxsplit[0]) == 7) {
      if (g_strncasecmp(rxsplit[0], "10", 2) == 0)
        write_text(rxbuf, DX10MESSAGE);
      else if (g_strncasecmp(rxsplit[0], "14", 2) == 0)
        write_text(rxbuf, DX14MESSAGE);
      else if (g_strncasecmp(rxsplit[0], "18", 2) == 0)
        write_text(rxbuf, DX18MESSAGE);
      else if (g_strncasecmp(rxsplit[0], "21", 2) == 0)
        write_text(rxbuf, DX21MESSAGE);
      else if (g_strncasecmp(rxsplit[0], "24", 2) == 0)
        write_text(rxbuf, DX24MESSAGE);
      else if (g_strncasecmp(rxsplit[0], "28", 2) == 0)
        write_text(rxbuf, DX28MESSAGE);
      else if (g_strncasecmp(rxsplit[0], "29", 2) == 0)
        write_text(rxbuf, DX28MESSAGE);
      else write_text(rxbuf, DXOTHERMESSAGE);
    }
    else
    write_text(rxbuf, DXOTHERMESSAGE);
    g_strfreev(rxsplit);
  }
  else
  write_text(rxbuf, RXMESSAGE);

  if (recording) if (write(fprec, rxbuf, numbytes) == -1) {
    writemessage(_("ERROR: Cannot record to file: %s\n"), g_strerror(errno), ERRORMESSAGE);
  }
}


void tx(GString *message) {
  GString *sendtext, *maintext;
  gint error;

  sendtext = g_string_new(message->str);
  maintext = g_string_new(message->str);
  (void *)g_string_append(sendtext, "\r");
  (void *)g_string_append(maintext, "\n");
  error = write(s, sendtext->str, sendtext->len);
  if (error == -1) 
    writemessage(_("ERROR: Cannot write to socket: %s\n"), g_strerror(errno), ERRORMESSAGE);
  else
    write_text(maintext->str, TXMESSAGE);
  g_string_free(sendtext, TRUE);
  g_string_free(maintext, TRUE);
}

/****************************** netrom related ******************************/
static gint nr_convert_call(gchar *nraddress)
{
  gchar nodesbuffer[100], *callalias;
  FILE *fp;
  gint nraddrlen, ret;
  gchar **nrlist;

  g_strup(nraddress);

  /* check if NET/ROM is supported */
  if ((fp = fopen("/proc/net/nr_nodes", "r")) == NULL) {
    writemessage(_("ERROR: NET/ROM not included in the kernel\n"), NULL, ERRORMESSAGE);
    return -1;
  }

  /* ignore first line */
  (void *)fgets(nodesbuffer, 100, fp);

  /* read the next and see if nraddress is actually in the node list */
  while (fgets(nodesbuffer, 100, fp) != NULL) {
    callalias = g_strdelimit(nodesbuffer, " \t\n\r", '#');
    nrlist = g_strsplit(callalias, "#", 2);
    if (g_strcasecmp(nraddress, list[0]) == 0 || g_strcasecmp(nraddress, list[1]) == 0) {
      nraddrlen = ax25_aton(list[0], &sockaddr.ax25);
      ret = fclose(fp);
      g_strfreev(nrlist);
      return (nraddrlen == -1) ? -1 : sizeof(struct sockaddr_ax25);
    }
    g_strfreev(nrlist);
  }

  ret = fclose(fp);
  writemessage(_("ERROR: NET/ROM callsign or alias not found\n"), NULL, ERRORMESSAGE);
  return -1;
}

/*********************** alarm handlers for the child ***********************/
static void connectalarmhandler() {
  gint error = -2, ret;

  /* ignore pending alarms and cleanup */
  (void *)signal(SIGHUP, SIG_IGN);
  (void *)signal(SIGALRM, SIG_IGN);
  (void *)alarm(0);

  /* write -2 to the pipe so the parent knows it is a timeout */
  ret = write(connectpipe[1], &error, sizeof(error));
  _exit(0);
}

static void huphandler() {
  gint error = -3, ret;

  /* ignore pending alarms and cleanup */
  (void *)signal(SIGALRM, SIG_IGN);
  (void *)signal(SIGHUP, SIG_IGN);
  (void *)alarm(0);

  /* write -3 to the pipe so the parent knows we want to kill the AX.25 connection */
  ret = write(connectpipe[1], &error, sizeof(error));
  _exit(0);
}

/***************** 'parent' functions related to the child ******************/
static gboolean childcheck() {
  gint status, childpid;

  /* free the child if it is a zombie */
  childpid = waitpid(-1, &status, WNOHANG);
  /* a FALSE value will stop the timer */
  return (WIFEXITED(status) == 0);
}


static void connectreadpipe() {
  gint error = 0, len = 0, ret;
  gchar *errorstr = NULL;

  /* connect has returned a result, we can stop the monitor */
  gdk_input_remove(connectmonitor);
  connecting = FALSE;

  /* read result of connection from the pipe */
  ret = read(connectpipe[0], &error, sizeof(error));

  /* check for error messages */
  if (error != 0) {
    writemessage(_("*** Cannot connect to %s: "), list[0], ERRORMESSAGE);
    if (error == -1) { /* we first receive length of string, than the string itself*/
      ret = read(connectpipe[0], &len, sizeof(len));
      errorstr = g_malloc(len + 1);
      ret = read(connectpipe[0], errorstr, len);
      writemessage("%s\n", errorstr, ERRORMESSAGE);
      g_free(errorstr);
    }
    if (error == -2) writemessage(_("Time-out during connection setup\n"), NULL, ERRORMESSAGE);
    if (error == -3) writemessage(_("Connection killed\n"), NULL, ERRORMESSAGE);
    if (list) g_strfreev(list);
    disconn();
  }
  else {
  connected = TRUE;
  ret = fcntl(s, F_SETFL, O_NONBLOCK);
  /* we are connected, set up the monitor for incoming packets */
  rxmonitor = gdk_input_add(s, GDK_INPUT_READ, GTK_SIGNAL_FUNC(rx), NULL);
  writemessage(_("--> Connected to %s\n"), list[0], STATUSMESSAGE);
  }
  ret = close(connectpipe[0]);
  ret = close(connectpipe[1]);
}

/******************** connect and disconnect routines ***********************/
static gint connectnonblock() {
  gint pid, error, len, ret;

  /* create fork */
  pid = fork();
  /* use child for the connection setup */
  if (pid == 0) {
    /* setup a timeout */
    (void *)signal(SIGALRM, connectalarmhandler);
    (void *)alarm(preferences.timeout);

    /* SIGHUP used to kill a connection */
    (void *)signal(SIGHUP, huphandler);

    error = connect(s, (struct sockaddr *)&sockaddr, addrlen);

    /* write errornumber to the pipe */
    ret = write(connectpipe[1], &error, sizeof(error));
    if (error == -1) { /* write length of errorstring and errorstring to pipe */
      len = strlen(g_strerror(errno)) + 1;
      ret = write(connectpipe[1], &len, sizeof(len));
      ret = write(connectpipe[1], g_strerror(errno), len);
    }
    _exit(0);
  }
  return(pid);
}

/* initialization and storage of ports, done at program startup */
void portsinit() {
  gchar *nextport = NULL;
  gint portnr;
  
  /* return the number of active AX.25 ports, must be called first */
  if (axports == 0) {
    if ((axports = ax25_config_load_ports()) == 0) {
      GString *info = g_string_new(_("ERROR: No AX.25 port data configured\n"));
      showdialog(info, _("%s - error"));
      g_string_free(info, TRUE);
      return;
    }
  }
  /* get number of active ports for NET/ROM and Rose */
  if (nrports == 0) nrports = nr_config_load_ports();
  if (rsports == 0) rsports = rs_config_load_ports();
  /* browse ports and put them in a list */
  if (axports != 0) {
  for (portnr = 0; portnr< axports; portnr++) {
      nextport = ax25_config_get_next(nextport);
      axportnames = g_list_append(axportnames, g_strdup(nextport));
    }
  }
  nextport = NULL;
  if (nrports != 0) {
    for (portnr = 0; portnr< nrports; portnr++) {
      nextport = nr_config_get_next(nextport);
      nrportnames = g_list_append(nrportnames, g_strdup(nextport));
      }
  }
  nextport = NULL;
  if (rsports != 0) {
    for (portnr = 0; portnr< rsports; portnr++) {
      nextport = rs_config_get_next(nextport);
      rsportnames = g_list_append(rsportnames, g_strdup(nextport));
    }
  }
}

void connectto(gchar *where) {
  GString *wherecopy, *toaddress;
  size_t len;
  gint error, ret, i, n;
  gchar *digi;

  /* split up the connect string, remove leading whitespace */
  wherecopy = g_string_new(g_strstrip(where));
  list = g_strsplit(wherecopy->str, " ", 1);
  port = g_strdup(list[0]);

  /* check out what protocol we have to use by comparing portname with available lists*/
  if ((n = g_list_length(axportnames)) > 0)
    for(i=0; i<n; i++) 
      if (g_strcasecmp(g_list_nth_data(axportnames, i), port) == 0) mode = AF_AX25;
  if ((n = g_list_length(nrportnames)) > 0)
    for(i=0; i<n; i++) 
      if (g_strcasecmp(g_list_nth_data(nrportnames, i), port) == 0) mode = AF_NETROM;
  if ((n = g_list_length(rsportnames)) > 0)
    for(i=0; i<n; i++) 
      if (g_strcasecmp(g_list_nth_data(rsportnames, i), port) == 0) mode = AF_ROSE;

  if (mode == 0) {
    writemessage(_("ERROR: Invalid port\n"), NULL, ERRORMESSAGE);
    g_strfreev(list);
    return;
  }
  else
  {
    af_mode = mode;
    mode = 0;
  }

  /* remove port from the connect string and copy destination to a list of pointers */
  g_strfreev(list);
  len = strlen(port);
  toaddress = g_string_erase(wherecopy, 0, len + 1);
  list = g_strsplit(toaddress->str, " ", 0);
  remotecallsign = g_strdup(list[0]);
  
  /* check number of argument, create socket and convert callsign to network format for each mode */
  switch (af_mode) {
    case AF_ROSE:
      if (list[0] == NULL || list[1] == NULL) {
        writemessage(_("ERROR: Too few arguments for Rose\n"), NULL, ERRORMESSAGE);
        return;
      }
      if ((s = socket(AF_ROSE, SOCK_SEQPACKET, 0)) < 0) {
        writemessage(_("ERROR: Cannot open socket for Rose: %s\n"), g_strerror(errno), ERRORMESSAGE);
        return;
      }
    break;
    case AF_NETROM:
      if (list[0] == NULL) {
        writemessage(_("ERROR: Too few arguments for NET/ROM\n"), NULL, ERRORMESSAGE);
        return;
      }
      if ((s = socket(AF_NETROM, SOCK_SEQPACKET, 0)) < 0) {
        writemessage(_("ERROR: Cannot open socket for NET/ROM: %s\n"), g_strerror(errno), ERRORMESSAGE);
        return;
      }
      (void)ax25_aton(nr_config_get_addr(port), &sockaddr.ax25);
      sockaddr.ax25.fsa_ax25.sax25_family = AF_NETROM;
      addrlen = sizeof(struct full_sockaddr_ax25);
    break;
    case AF_AX25:
      if (list[0] == NULL) {
        writemessage(_("ERROR: Too few arguments for AX.25\n"), NULL, ERRORMESSAGE);
        return;
      }
      if ((s = socket(AF_AX25, SOCK_SEQPACKET, 0)) < 0) {
        writemessage(_("ERROR: Cannot open socket for AX.25: %s\n"), g_strerror(errno), ERRORMESSAGE);
        return;
      }
      (void)ax25_aton(ax25_config_get_addr(port), &sockaddr.ax25);
      if (sockaddr.ax25.fsa_ax25.sax25_ndigis == 0) {
        (void)ax25_aton_entry(ax25_config_get_addr(port), sockaddr.ax25.fsa_digipeater[0].ax25_call);
        sockaddr.ax25.fsa_ax25.sax25_ndigis = 1;
      }
      sockaddr.ax25.fsa_ax25.sax25_family = AF_AX25;
      addrlen = sizeof(struct full_sockaddr_ax25);
    break;
  }

  /* Let Rose autobind */
  if (af_mode != AF_ROSE) {
    if (bind(s, (struct sockaddr *) &sockaddr, addrlen) != 0) {
      writemessage(_("ERROR: Cannot bind socket: %s\n"), g_strerror(errno), ERRORMESSAGE);
      ret = close(s);
      return;
    }
  }

  /* convert list into structure understood by the kernel */
  switch (af_mode) {
    case AF_ROSE:
      memset(&sockaddr.rose, 0x00, sizeof(struct sockaddr_rose));
      if (ax25_aton_entry(list[0], sockaddr.rose.srose_call.ax25_call) == -1) {
        ret = close(s);
        return;
      }
      if (rose_aton(list[1], sockaddr.rose.srose_addr.rose_addr) == -1) {
        ret = close(s);
        return;
      }
      if (list[2] != NULL) {
        digi = list[2];
        if (g_strcasecmp(list[2], "VIA") == 0) {
          if (list[3] == NULL) {
            writemessage(_("ERROR: Callsign must follow 'via'\n"), NULL, ERRORMESSAGE);
            ret = close(s);
            return;
          }
        digi = list[3];
        }
        if (ax25_aton_entry(digi, sockaddr.rose.srose_digi.ax25_call) == -1) {
          ret = close(s);
          return;
        }
        sockaddr.rose.srose_ndigis = 1;
      }
      sockaddr.rose.srose_family = AF_ROSE;
      addrlen = sizeof(struct sockaddr_rose);
    break;
    case AF_NETROM:
      if (nr_convert_call(list[0]) == -1) {
        ret = close(s);
        return;
      }
      sockaddr.rose.srose_family = AF_NETROM;
      addrlen = sizeof(struct sockaddr_ax25);
    break;
    case AF_AX25:
      if (ax25_aton_arglist((const gchar**)list, &sockaddr.ax25) == -1) {
        writemessage(_("ERROR: Invalid destination callsign or digipeater\n"), NULL, ERRORMESSAGE);
        ret = close(s);
        return;
      }
      sockaddr.rose.srose_family = AF_AX25;
      addrlen = sizeof(struct full_sockaddr_ax25);
    break;
  }

  writemessage(_("--> Connection setup...\n"), NULL, STATUSMESSAGE);

  /* create pipe for nonblocking connection setup */
  error = pipe(connectpipe);
  if (error != 0) {
    writemessage(_("ERROR: pipe() failed: %s\n"), g_strerror(errno), ERRORMESSAGE);
    return;
  }

  /* make the actual connection, return pid of child */
  connectpid = connectnonblock();

  if (connectpid == -1) {
    writemessage(_("ERROR: fork() failed: %s\n"), g_strerror(errno), ERRORMESSAGE);
    return;
  }

  connecting = TRUE;
  /* monitor one end of the pipe */
  connectmonitor = gdk_input_add(connectpipe[0], GDK_INPUT_READ, GTK_SIGNAL_FUNC(connectreadpipe), NULL);
  /* check every second if the child has finished until childcheck returns FALSE, so the timer will be stopped */
  (void)gtk_timeout_add(1000, childcheck, NULL);
}


void disconn() {
  gint error;
  
  error = close(s);
  if (error == -1) {
    writemessage(_("ERROR: Socket close failed: %s\n"), g_strerror(errno), ERRORMESSAGE);
    return;
  }
  if (rxmonitor > 0) gdk_input_remove(rxmonitor);
  rxmonitor = 0;
  writemessage(_("--> Disconnected\n"), NULL, STATUSMESSAGE);
  connected = FALSE;
}

void killconn() {
  if (connecting) kill(connectpid, SIGHUP);
}

Generated by  Doxygen 1.6.0   Back to index