Logo Search packages:      
Sourcecode: farsight version File versions

upnpigd.c

/*
 * upnpigd.c - Source for clinkc wrapper
 *
 * Farsight helper
 * Copyright (C) 2005,2006 Collabora Ltd.
 * Copyright (C) 2005,2006 Nokia Corporation
 *   @author Philippe Kalaf <philippe.kalaf@collabora.co.uk>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser 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
 */

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <cybergarage/upnp/cupnp.h>

#include <netinet/in.h>

typedef struct {
  char* remote_host;
  int remote_port;
  char* protocol;
  char* internal_client;
  int internal_port;
  BOOL enabled;
  char* description;
  long int duration;
} PortMapping;

CgUpnpControlPoint *chavoCP = NULL;

/* Device/service types and action names */
#define IGD_DEVICE_TYPE "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
#define IPCONNECTION_SERVICE_TYPE "urn:schemas-upnp-org:service:WANIPConnection"
#define PPPCONNECTION_SERVICE_TYPE "urn:schemas-upnp-org:service:WANPPPConnection"
#define ADD_PORT_MAP_ACTION "AddPortMapping"
#define DEL_PORT_MAP_ACTION "DeletePortMapping"

/* UPnP IGD errors */
#define UPNP_IGD_ERROR_NO_SUCH_ENTRY_IN_ARRAY               714
#define UPNP_IGD_ERROR_REMOTE_HOST_ONLY_SUPPORTS_WILDCARD   726
#define UPNP_IGD_ERROR_EXTERNAL_PORT_ONLY_SUPPORTS_WILDCARD 727

#define BUFSIZE 128

/* Errors are printed out if PRINT_ERRORS is defined
#define PRINT_ERRORS
*/

#ifdef PRINT_ERRORS
#define upnp_error(format, modifiers...) printf(format, modifiers)
#else
#define upnp_error(format, modifiers...)
#endif

/**
  Delete port mapping specified by remote host, port and protocol.
  */
static BOOL del_port_mapping(CgUpnpService *conn, 
    const char *remote_host, 
    int remote_port,
    const char *protocol);
/**
  Add specified portmapping to WANIPConnection
  */
static BOOL add_port_mapping(CgUpnpService *conn, 
    const PortMapping *mapping);

static BOOL del_port_mapping(CgUpnpService* conn, 
    const char* remote_host,
    int remote_port,
    const char* protocol)
{
  char buf[BUFSIZE];
  CgUpnpArgument* arg = NULL;
  CgUpnpAction* action = NULL;
  BOOL result = FALSE;

  /* Get action */
  action = cg_upnp_service_getactionbyname(conn, DEL_PORT_MAP_ACTION);
  if (action == NULL) {
    upnp_error("Action %s not found in service!\n", DEL_PORT_MAP_ACTION);
    return FALSE;
  }

  /* Set host, external port and protocol */
  arg = cg_upnp_action_getargumentbyname(action, "NewRemoteHost");
  cg_upnp_argument_setvalue(arg, strdup(remote_host));

  arg = cg_upnp_action_getargumentbyname(action, "NewExternalPort");
  snprintf(buf, BUFSIZE, "%d", remote_port);
  cg_upnp_argument_setvalue(arg, buf);

  arg = cg_upnp_action_getargumentbyname(action, "NewProtocol");
  cg_upnp_argument_setvalue(arg, strdup(protocol));

  /* Post action and get error */
  result = cg_upnp_action_post(action);
  if (result == FALSE)
  {
    upnp_error("Statuscode:%i, desc: %s\n", 
        cg_upnp_action_getstatuscode(action),
        cg_upnp_action_getstatusdescription(action));

    if (cg_upnp_action_getstatuscode(action) == 
        UPNP_IGD_ERROR_NO_SUCH_ENTRY_IN_ARRAY)
    {
      /* Set remote host to empty (wildcard) */
      arg = cg_upnp_action_getargumentbyname(action, 
          "NewRemoteHost");
      if (arg != NULL) 
        cg_upnp_argument_setvalue(arg, "");

      result = cg_upnp_action_post(action);           
    }

  }

  return result;
}


static BOOL add_port_mapping(CgUpnpService* conn, 
    const PortMapping* mapping)
{
  char buf[BUFSIZE];
  CgUpnpArgument* arg = NULL;
  CgUpnpAction* action = NULL;
  BOOL result = FALSE;

  if (mapping == NULL) return FALSE;

  /* Get action */
  action = cg_upnp_service_getactionbyname(conn, ADD_PORT_MAP_ACTION);
  if (action == NULL) {
    upnp_error("Action %s not found in service!\n", ADD_PORT_MAP_ACTION);
    return FALSE;
  }

  /* Fill in arguments */

  /* Set remote host */
  arg = cg_upnp_action_getargumentbyname(action, "NewRemoteHost");
  if (arg != NULL) 
    cg_upnp_argument_setvalue(arg, mapping->remote_host);

  /* Set external port */
  arg = cg_upnp_action_getargumentbyname(action, "NewExternalPort");
  snprintf(buf, BUFSIZE, "%d", mapping->remote_port);
  if (arg != NULL) cg_upnp_argument_setvalue(arg, buf);

  /* Set protocol */
  arg = cg_upnp_action_getargumentbyname(action, "NewProtocol");
  if (arg != NULL) cg_upnp_argument_setvalue(arg, mapping->protocol);

  /* Set internal client */
  arg = cg_upnp_action_getargumentbyname(action,
      "NewInternalClient");
  if (arg != NULL) 
    cg_upnp_argument_setvalue(arg, mapping->internal_client);

  /* Set internal port */
  arg = cg_upnp_action_getargumentbyname(action,
      "NewInternalPort");
  snprintf(buf, BUFSIZE, "%d", mapping->internal_port);
  if (arg != NULL) cg_upnp_argument_setvalue(arg, buf);

  /* Set enabled */
  arg = cg_upnp_action_getargumentbyname(action,
      "NewEnabled");
  snprintf(buf, BUFSIZE, "%d", mapping->enabled);
  if (arg != NULL) cg_upnp_argument_setvalue(arg, buf);

  /* Set description */
  arg = cg_upnp_action_getargumentbyname(action,
      "NewPortMappingDescription");
  if (arg != NULL) 
    cg_upnp_argument_setvalue(arg, mapping->description);

  /* Set lease duration */
  arg = cg_upnp_action_getargumentbyname(action,
      "NewLeaseDuration");
  snprintf(buf, BUFSIZE, "%ld", mapping->duration);
  if (arg != NULL) cg_upnp_argument_setvalue(arg, buf);

  /* Post action and handle the result */
  result = cg_upnp_action_post(action);
  if (result == FALSE)
  {
    upnp_error("Statuscode:%i, desc: %s\n", 
        cg_upnp_action_getstatuscode(action),
        cg_upnp_action_getstatusdescription(action));

    /* Handle remote host only support wildcard error */
    if (cg_upnp_action_getstatuscode(action) == 
        UPNP_IGD_ERROR_REMOTE_HOST_ONLY_SUPPORTS_WILDCARD)
    {
      /* Set remote host to empty (wildcard) */
      arg = cg_upnp_action_getargumentbyname(action, 
          "NewRemoteHost");
      if (arg != NULL) 
        cg_upnp_argument_setvalue(arg, "");

      result = cg_upnp_action_post(action);           
    }

    /* Handle external port only support wildcard error */
    if (cg_upnp_action_getstatuscode(action) == 
        UPNP_IGD_ERROR_EXTERNAL_PORT_ONLY_SUPPORTS_WILDCARD)
    {
      /* Set remote port to zero (wildcard) */
      arg = cg_upnp_action_getargumentbyname(action, 
          "NewExternalPort");
      if (arg != NULL) 
        cg_upnp_argument_setvalue(arg, "0");

      result = cg_upnp_action_post(action);           
    }

    /* Handle case when some of the args is not accepted */
    if (cg_upnp_action_getstatuscode(action) == 
        CG_UPNP_STATUS_INVALID_ARGS)
    {
      arg = cg_upnp_action_getargumentbyname(action,
          "NewRemoteHost");
      /* in real scenario we should check, which arg is
         empty */
      if (arg != NULL)
        cg_upnp_argument_setvalue(arg, " ");

      result = cg_upnp_action_post(action);
    }
  }

  return result;
}

int upnp_open_port_all_igds(const char *internal_ip_address, 
    int internal_port,
    int external_port,
    int protocol)
{
  PortMapping port_mapping;
  CgUpnpDevice *dev;
  CgUpnpService *conn = NULL;

  if ( NULL == chavoCP )
  {
    upnp_error("Stack is not yet initialized.");
    return 0;
  }

  upnp_error("opening int_ip %s, int_port %d, ext_port %d, protocol %d",
      internal_ip_address, internal_port, external_port,
      protocol);

  port_mapping.remote_host = "";
  port_mapping.remote_port = external_port;
  port_mapping.protocol = (protocol == 1 ? "TCP" : "UDP");;
  port_mapping.internal_client = (char *)internal_ip_address;
  port_mapping.internal_port = internal_port;
  port_mapping.enabled = TRUE;
  port_mapping.description = "Voip";
  port_mapping.duration = 0;

  cg_upnp_controlpoint_lock(chavoCP);
  for (dev = cg_upnp_controlpoint_getdevices(chavoCP);
      dev != NULL; dev = cg_upnp_device_next(dev))
  {
    /* Note: Only version 1 of interface is supported. */
    if (cg_upnp_device_isdevicetype(dev, IGD_DEVICE_TYPE) == FALSE)
      continue;


    /* Get IGD device and WANIPConnection service to open/close the
       preferred port */

    conn = cg_upnp_device_getservicebytype(dev,
        IPCONNECTION_SERVICE_TYPE);

    if (conn == NULL)
    {
        upnp_error("Error getting %s, trying %s still before giving up.\n", 
            IPCONNECTION_SERVICE_TYPE, PPPCONNECTION_SERVICE_TYPE);
        conn = cg_upnp_device_getservicebytype(dev, PPPCONNECTION_SERVICE_TYPE);
    }


    if (conn == NULL)
    {
      upnp_error("Error getting %s\n", PPPCONNECTION_SERVICE_TYPE);
      continue;
    }

    /* Do the task */
    upnp_error("[%s] ", cg_upnp_device_getfriendlyname(dev));
    if (!add_port_mapping(conn, &port_mapping))
    {
      upnp_error("Adding port mapping failed. Is it already "
          "        opened?\n");
    } else  {
      upnp_error("Port mapping added.\n");
    }
  }

  cg_upnp_controlpoint_unlock(chavoCP);

  return 1;
}

void upnp_close_port_all_igds(int external_port, int protocoln)
{
  int port = external_port;
  int protocolNum = protocoln;
  char *protocol;

  CgUpnpDevice *dev;
  CgUpnpService *conn = NULL;

  if ( NULL == chavoCP )
  {
    upnp_error("Stack is not yet initialized.");
    return;
  }

  protocol = IPPROTO_UDP == protocolNum ? "UDP" : "TCP";

  cg_upnp_controlpoint_lock(chavoCP);

  for (dev = cg_upnp_controlpoint_getdevices(chavoCP);
      dev != NULL; dev = cg_upnp_device_next(dev))
  {
    if (cg_upnp_device_isdevicetype(dev, IGD_DEVICE_TYPE) == FALSE)
      continue;


    /* Get IGD device and WANIPConnection service to open/close the
       preferred port */

    conn = cg_upnp_device_getservicebytype(dev, IPCONNECTION_SERVICE_TYPE);

    if (conn == NULL)
    {
        upnp_error("Error getting %s, trying %s still before giving up.\n", 
            IPCONNECTION_SERVICE_TYPE, PPPCONNECTION_SERVICE_TYPE);
      conn = cg_upnp_device_getservicebytype(dev, PPPCONNECTION_SERVICE_TYPE);
    }


    if (conn == NULL)
    {
      upnp_error("Error getting %s\n", PPPCONNECTION_SERVICE_TYPE);
      continue;
    }
  
    /* Do the task */
    upnp_error("[%s] ",
        cg_upnp_device_getfriendlyname(dev));
    if (!del_port_mapping(conn, "", port, protocol))
    {
      upnp_error("Deleting the port mapping failed. Does it "
          "exist?\n");
    } else {
      upnp_error("Port mapping deleted.\n");
    }

  }

  cg_upnp_controlpoint_unlock(chavoCP);
}

int upnp_cp_init()
{
  int ret_val = 1;

  if ( NULL == chavoCP )
  {
    chavoCP = cg_upnp_controlpoint_new();
    if ( FALSE == cg_upnp_controlpoint_start(chavoCP ) ) {
      upnp_error("Couldn't start this control point !\n");
      return 0;
    }

    ret_val = cg_upnp_controlpoint_search(chavoCP, IGD_DEVICE_TYPE);

    /* Note: Search is asynchronous and it takes (usually) couple of seconds
     * before all devices are fully discovered.
     */
  }

  return ret_val;
}

int upnp_cp_shutdown()
{
  int ret_val = 0;

  if ( NULL != chavoCP )
  {
    ret_val = cg_upnp_controlpoint_stop(chavoCP);
    cg_upnp_controlpoint_delete(chavoCP);
    chavoCP = NULL;
  }

  return ret_val;
}

Generated by  Doxygen 1.6.0   Back to index