msousa@0: /* msousa@0: * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt) msousa@0: * msousa@0: * This file is part of the Modbus library for Beremiz and matiec. msousa@0: * msousa@0: * This Modbus library is free software: you can redistribute it and/or modify msousa@0: * it under the terms of the GNU Lesser General Public License as published by msousa@0: * the Free Software Foundation, either version 3 of the License, or msousa@0: * (at your option) any later version. msousa@0: * msousa@0: * This program is distributed in the hope that it will be useful, but msousa@0: * WITHOUT ANY WARRANTY; without even the implied warranty of msousa@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser msousa@0: * General Public License for more details. msousa@0: * msousa@0: * You should have received a copy of the GNU Lesser General Public License msousa@0: * along with this Modbus library. If not, see . msousa@0: * msousa@0: * This code is made available on the understanding that it will not be msousa@0: * used in safety-critical situations without a full and competent review. msousa@0: */ msousa@0: msousa@0: msousa@0: /* sin_util.c */ msousa@0: msousa@0: #include "sin_util.h" msousa@0: msousa@0: #include msousa@0: #include msousa@0: #include msousa@0: #include // gethostbyname(), ... msousa@0: #include // errno msousa@0: #include // strtoll() msousa@0: #include // isspace() msousa@0: #include // memcpy(), memset() msousa@0: #include // strcasecmp() msousa@0: #include // perror() msousa@0: msousa@0: #ifndef INADDR_NONE msousa@0: #define INADDR_NONE 0xffffffff msousa@0: #endif msousa@0: msousa@0: /* Last time I (msousa) checked, this was not being defined in QNX */ msousa@0: #ifndef socklen_t msousa@0: typedef unsigned int socklen_t; msousa@0: #endif msousa@0: msousa@0: msousa@0: static inline int str_is_whitespace(const char *str) { msousa@0: const char *s; msousa@0: for (s = str; *s; s++) if (!isspace(*s)) return 0; // non whitespace char found msousa@0: return 1; // all whitespace, or empty "" msousa@0: } msousa@0: msousa@0: /* Convert a string to a number, allowing for leading and trailing whitespace */ msousa@0: /* Number may be in decimal, hexadecimal (leading '0x') or octal (leading '0') format */ msousa@0: static long long str_to_portnum(const char *str) { msousa@0: long long port = 0; msousa@0: char *errstr; msousa@0: #define PORT_MIN 0 msousa@0: #define PORT_MAX 65535 msousa@0: errno = 0; // strtoll() does not set errno to 0 on success!! msousa@0: port = strtoll(str, &errstr, 0 /* accept base 8, 10 and 16 */); msousa@0: if (str == errstr) return -1; // not a number msousa@0: if (errno == ERANGE) return -2; // out of range msousa@0: if (!str_is_whitespace(errstr)) return -1; // not a number (has trailing characters) msousa@0: if ((port < PORT_MIN) || (port > PORT_MAX)) return -2; // out of range msousa@0: return (port); msousa@0: } msousa@0: msousa@0: msousa@0: msousa@0: static int gettypebyname(const char *protocol) { msousa@0: if (strcasecmp(protocol, "tcp") == 0) return SOCK_STREAM; msousa@0: if (strcasecmp(protocol, "udp") == 0) return SOCK_DGRAM; msousa@0: if (strcasecmp(protocol, "raw") == 0) return SOCK_RAW; msousa@0: return SOCK_PACKET; // a deprecated service type, we use here as error. msousa@0: } msousa@0: msousa@0: msousa@0: static int getportbyname(in_port_t *port, const char *service, const char *protocol) { msousa@0: struct servent *se; msousa@0: int32_t tmp; msousa@0: // if service is NULL, "" or string of whitespace, then set port to 0, and return -1. msousa@0: // Used when binding to a random port on the local host... msousa@0: if ( port == NULL) { return -1;} msousa@0: if ( service == NULL) {*port = 0; return -1;} msousa@0: if (str_is_whitespace(service)) {*port = 0; return -1;} msousa@0: if ((se = getservbyname(service, protocol)) != NULL) {*port = se->s_port; return 0;} msousa@0: if ((tmp = str_to_portnum(service)) >= 0) {*port = htons((uint16_t)tmp); return 0;} msousa@0: return -2; msousa@0: } msousa@0: msousa@0: msousa@0: static int getipbyname(struct in_addr *ip_addr, const char *host) { msousa@0: struct hostent *he; msousa@0: // if host is NULL, "", or "*", then set ip_addr to INADDR_ANY, and return -1. msousa@0: // Used when binding to all interfaces on the local host... msousa@0: if ( host == NULL) {ip_addr->s_addr = INADDR_ANY; return -1;} msousa@0: if (str_is_whitespace(host)) {ip_addr->s_addr = INADDR_ANY; return -1;} msousa@0: if (strcmp(host, "*") == 0) {ip_addr->s_addr = INADDR_ANY; return -1;} msousa@0: if ((he = gethostbyname(host)) != NULL) {memcpy((char *)ip_addr, he->h_addr, he->h_length); return 0;} msousa@0: if ((ip_addr->s_addr = inet_addr(host)) != INADDR_NONE) {return 0;} msousa@0: return -2; msousa@0: } msousa@0: msousa@0: msousa@0: msousa@0: msousa@0: msousa@0: int sin_initaddr(struct sockaddr_in *sin, msousa@0: const char *host, int allow_null_host, // 1 => allow host NULL, "" or "*" -> INADDR_ANY msousa@0: const char *service, int allow_null_serv, // 1 => allow serivce NULL or "" -> port = 0 msousa@0: const char *protocol) { msousa@0: int he = allow_null_host?-1:0; msousa@0: int se = allow_null_serv?-1:0; msousa@0: msousa@0: memset((void *)sin, 0, sizeof(sin)); msousa@0: sin->sin_family = AF_INET; msousa@0: msousa@0: if (getportbyname(&(sin->sin_port), service, protocol) < se) return -1; msousa@0: if (getipbyname (&(sin->sin_addr), host) < he) return -1; msousa@0: return 0; msousa@0: } msousa@0: msousa@0: msousa@0: msousa@0: /* Create a socket for the IP protocol family, and connect to remote host. */ msousa@0: int sin_connsock(const char *host, const char *service, const char *protocol) { msousa@0: struct sockaddr_in sin; msousa@0: int s, type; msousa@0: msousa@0: if (sin_initaddr(&sin, host, 0, service, 0, protocol) < 0) return -1; msousa@0: if ((type = gettypebyname(protocol)) == SOCK_PACKET) return -1; msousa@0: /* create the socket */ msousa@0: if ((s = socket(PF_INET, type, 0)) < 0) {perror("socket()"); return -1;} msousa@0: /* connect the socket */ msousa@0: if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {perror("connect()"); return -1;} msousa@0: msousa@0: return(s); msousa@0: } msousa@0: msousa@0: msousa@0: msousa@0: msousa@0: /* Create a socket for the IP protocol family, and bind to local host's port. */ msousa@0: int sin_bindsock(const char *host, const char *service, const char *protocol) { msousa@0: struct sockaddr_in sin; msousa@0: int s, type; msousa@0: msousa@0: // TODO allow random port... Needs new input parameter to function interface! msousa@0: if (sin_initaddr(&sin, host, 1, service, 0, protocol) < 0) return -1; msousa@0: if ((type = gettypebyname(protocol)) == SOCK_PACKET) return -1; msousa@0: /* create the socket */ msousa@0: if ((s = socket(PF_INET, type, 0)) < 0) {perror("socket()"); return -1;} Edouard@3: if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) { Edouard@3: perror("setsockopt(SO_REUSEADDR) failed"); Edouard@3: return -1; Edouard@3: } msousa@0: /* bind the socket */ msousa@0: if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {perror("bind()"); return -1;} msousa@0: msousa@0: return(s); msousa@0: } msousa@0: msousa@0: msousa@0: msousa@0: