sin_util.c
changeset 0 ae252e0fd9b8
child 3 1223f413e054
equal deleted inserted replaced
-1:000000000000 0:ae252e0fd9b8
       
     1 /*
       
     2  * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
       
     3  *
       
     4  * This file is part of the Modbus library for Beremiz and matiec.
       
     5  *
       
     6  * This Modbus library is free software: you can redistribute it and/or modify
       
     7  * it under the terms of the GNU Lesser General Public License as published by
       
     8  * the Free Software Foundation, either version 3 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, but
       
    12  * WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
       
    14  * General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Lesser General Public License
       
    17  * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
       
    18  *
       
    19  * This code is made available on the understanding that it will not be
       
    20  * used in safety-critical situations without a full and competent review.
       
    21  */
       
    22 
       
    23 
       
    24 /* sin_util.c */
       
    25 
       
    26 #include "sin_util.h"
       
    27 
       
    28 #include <sys/types.h>
       
    29 #include <sys/socket.h>
       
    30 #include <netinet/in.h>
       
    31 #include <netdb.h>   // gethostbyname(), ...
       
    32 #include <errno.h>   // errno
       
    33 #include <stdlib.h>  // strtoll()
       
    34 #include <ctype.h>   // isspace()
       
    35 #include <string.h>  // memcpy(), memset()
       
    36 #include <strings.h> // strcasecmp()
       
    37 #include <stdio.h>   // perror()
       
    38 
       
    39 #ifndef INADDR_NONE
       
    40 #define INADDR_NONE       0xffffffff
       
    41 #endif
       
    42 
       
    43 /* Last time I (msousa) checked, this was not being defined in QNX */
       
    44 #ifndef socklen_t
       
    45 typedef unsigned int socklen_t;
       
    46 #endif
       
    47 
       
    48 
       
    49 static inline int str_is_whitespace(const char *str) {
       
    50   const char *s;
       
    51   for (s = str; *s; s++) if (!isspace(*s)) return 0; // non whitespace char found
       
    52   return 1; // all whitespace, or empty ""
       
    53 }
       
    54 
       
    55 /* Convert a string to a number, allowing for leading and trailing whitespace */
       
    56 /* Number may be in decimal, hexadecimal (leading '0x') or octal (leading '0') format */
       
    57 static long long str_to_portnum(const char *str) {
       
    58   long long port = 0;
       
    59   char *errstr;
       
    60   #define PORT_MIN 0
       
    61   #define PORT_MAX 65535
       
    62   errno = 0; // strtoll() does not set errno to 0 on success!!
       
    63   port = strtoll(str, &errstr, 0 /* accept base 8, 10 and 16 */);
       
    64   if (str   == errstr)                        return -1; // not a number
       
    65   if (errno == ERANGE)                        return -2; // out of range
       
    66   if (!str_is_whitespace(errstr))             return -1; // not a number (has trailing characters)
       
    67   if ((port < PORT_MIN) || (port > PORT_MAX)) return -2; // out of range  
       
    68   return (port);
       
    69 }
       
    70 
       
    71 
       
    72 
       
    73 static int gettypebyname(const char *protocol) {
       
    74    if (strcasecmp(protocol, "tcp") == 0) return SOCK_STREAM;
       
    75    if (strcasecmp(protocol, "udp") == 0) return SOCK_DGRAM;
       
    76    if (strcasecmp(protocol, "raw") == 0) return SOCK_RAW;
       
    77    return SOCK_PACKET; // a deprecated service type, we use here as error.
       
    78 }
       
    79 
       
    80 
       
    81 static int getportbyname(in_port_t *port, const char *service, const char *protocol) {
       
    82    struct servent *se;
       
    83    int32_t tmp;   
       
    84    // if service is NULL, "" or string of whitespace, then set port to 0, and return -1.
       
    85    // Used when binding to a random port on the local host...
       
    86    if (    port == NULL)                                 {                              return -1;}
       
    87    if ( service == NULL)                                 {*port = 0;                    return -1;}
       
    88    if (str_is_whitespace(service))                       {*port = 0;                    return -1;}
       
    89    if ((se  = getservbyname(service, protocol)) != NULL) {*port = se->s_port;           return  0;}
       
    90    if ((tmp = str_to_portnum(service)) >= 0)             {*port = htons((uint16_t)tmp); return  0;}
       
    91    return -2;
       
    92 }
       
    93 
       
    94 
       
    95 static int getipbyname(struct in_addr *ip_addr, const char *host) {
       
    96    struct hostent *he;
       
    97    // if host is NULL, "", or "*", then set ip_addr to INADDR_ANY, and return -1.
       
    98    // Used when binding to all interfaces on the local host...
       
    99    if ( host == NULL)                                 {ip_addr->s_addr = INADDR_ANY; return -1;}
       
   100    if (str_is_whitespace(host))                       {ip_addr->s_addr = INADDR_ANY; return -1;}
       
   101    if (strcmp(host, "*") == 0)                        {ip_addr->s_addr = INADDR_ANY; return -1;}
       
   102    if ((he = gethostbyname(host)) != NULL) {memcpy((char *)ip_addr, he->h_addr, he->h_length); return 0;}
       
   103    if ((ip_addr->s_addr = inet_addr(host)) != INADDR_NONE) {return 0;}
       
   104    return -2;
       
   105 }
       
   106 
       
   107 
       
   108 
       
   109 
       
   110 
       
   111 int sin_initaddr(struct sockaddr_in *sin,
       
   112                   const char *host,    int allow_null_host, // 1 => allow host NULL, "" or "*" -> INADDR_ANY
       
   113                   const char *service, int allow_null_serv, // 1 => allow serivce NULL or ""   -> port = 0
       
   114                   const char *protocol) {
       
   115   int he = allow_null_host?-1:0;
       
   116   int se = allow_null_serv?-1:0;
       
   117 
       
   118   memset((void *)sin, 0, sizeof(sin));
       
   119   sin->sin_family = AF_INET;
       
   120   
       
   121   if (getportbyname(&(sin->sin_port), service, protocol) < se) return -1; 
       
   122   if (getipbyname  (&(sin->sin_addr), host)              < he) return -1;
       
   123   return 0;
       
   124 }
       
   125 
       
   126 
       
   127 
       
   128 /* Create a socket for the IP protocol family, and connect to remote host. */
       
   129 int sin_connsock(const char *host, const char *service, const char *protocol) {
       
   130   struct sockaddr_in sin;
       
   131   int s, type;
       
   132 
       
   133   if (sin_initaddr(&sin, host, 0, service, 0, protocol) < 0) return -1;
       
   134   if ((type = gettypebyname(protocol)) == SOCK_PACKET)       return -1;
       
   135   /* create the socket */
       
   136   if ((s = socket(PF_INET, type, 0)) < 0)                   {perror("socket()");  return -1;}   
       
   137   /* connect the socket */
       
   138   if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {perror("connect()"); return -1;}
       
   139 
       
   140   return(s);
       
   141 }
       
   142 
       
   143 
       
   144 
       
   145 
       
   146 /* Create a socket for the IP protocol family, and bind to local host's port. */
       
   147 int sin_bindsock(const char *host, const char *service, const char *protocol) {
       
   148   struct sockaddr_in sin;
       
   149   int s, type;
       
   150 
       
   151   // TODO allow random port... Needs new input parameter to function interface!
       
   152   if (sin_initaddr(&sin, host, 1, service, 0, protocol) < 0) return -1;
       
   153   if ((type = gettypebyname(protocol)) == SOCK_PACKET)       return -1;
       
   154   /* create the socket */
       
   155   if ((s = socket(PF_INET, type, 0)) < 0)                   {perror("socket()");  return -1;}   
       
   156   /* bind the socket */
       
   157   if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0)   {perror("bind()");    return -1;}
       
   158   
       
   159   return(s);
       
   160 }
       
   161 
       
   162 
       
   163 
       
   164