| /* sckt.c provide access to the socket layer. |
| |
| Copyright (C) 2005-2022 Free Software Foundation, Inc. |
| Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>. |
| |
| This file is part of GNU Modula-2. |
| |
| GNU Modula-2 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 3, or (at your option) |
| any later version. |
| |
| GNU Modula-2 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. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <config.h> |
| #include <m2rts.h> |
| |
| #define EXPORT(FUNC) m2pim ## _sckt_ ## FUNC |
| #define M2EXPORT(FUNC) m2pim ## _M2_sckt_ ## FUNC |
| #define M2LIBNAME "m2pim" |
| |
| #if defined(HAVE_SYS_TYPES_H) |
| #include <sys/types.h> |
| #endif |
| |
| #if defined(HAVE_SYS_SOCKET_H) |
| #include <sys/socket.h> |
| #endif |
| |
| #if defined(HAVE_NETINET_IN_H) |
| #include <netinet/in.h> |
| #endif |
| |
| #if defined(HAVE_NETDB_H) |
| #include <netdb.h> |
| #endif |
| |
| #if defined(HAVE_UNISTD_H) |
| #include <unistd.h> |
| #endif |
| |
| #if defined(HAVE_SIGNAL_H) |
| #include <signal.h> |
| #endif |
| |
| #if defined(HAVE_SYS_ERRNO_H) |
| #include <sys/errno.h> |
| #endif |
| |
| #if defined(HAVE_ERRNO_H) |
| #include <errno.h> |
| #endif |
| |
| #if defined(HAVE_MALLOC_H) |
| #include <malloc.h> |
| #endif |
| |
| #if defined(HAVE_STRING_H) |
| #include <string.h> |
| #endif |
| |
| #if defined(HAVE_STDLIB_H) |
| #include <stdlib.h> |
| #endif |
| |
| #if defined(HAVE_STDIO_H) |
| #include <stdio.h> |
| #endif |
| |
| #define PORTSTART 7000 |
| #define NOOFTRIES 100 |
| #define MAXHOSTNAME 256 |
| |
| #undef DEBUGGING |
| |
| #if defined(HAVE_SYS_SOCKET_H) |
| |
| #define ERROR(X) \ |
| { \ |
| printf ("%s:%d:%s\n", __FILE__, __LINE__, X); \ |
| localExit (1); \ |
| } |
| |
| #define ASSERT(X) \ |
| { \ |
| if (!(X)) \ |
| { \ |
| printf ("%s:%d: assert(%s) failed\n", __FILE__, __LINE__, #X); \ |
| exit (1); \ |
| } \ |
| } |
| |
| typedef struct |
| { |
| char hostname[MAXHOSTNAME]; |
| struct hostent *hp; |
| struct sockaddr_in sa, isa; |
| int sockFd; |
| int portNo; |
| } tcpServerState; |
| |
| int |
| localExit (int i) |
| { |
| exit (1); |
| } |
| |
| /* tcpServerEstablishPort returns a tcpState containing the relevant |
| information about a socket declared to receive tcp connections. |
| This method attempts to use the port specified by the parameter. */ |
| |
| extern "C" tcpServerState * |
| EXPORT(tcpServerEstablishPort) (int portNo) |
| { |
| tcpServerState *s = (tcpServerState *)malloc (sizeof (tcpServerState)); |
| int b, p, n; |
| |
| if (s == NULL) |
| ERROR ("no more memory"); |
| |
| /* Remove SIGPIPE which is raised on the server if the client is killed. */ |
| signal (SIGPIPE, SIG_IGN); |
| |
| if (gethostname (s->hostname, MAXHOSTNAME) < 0) |
| ERROR ("cannot find our hostname"); |
| |
| s->hp = gethostbyname (s->hostname); |
| if (s->hp == NULL) |
| ERROR ("cannot get host name"); |
| |
| p = -1; |
| n = 0; |
| do |
| { |
| p++; |
| /* Open a TCP socket (an Internet stream socket). */ |
| |
| s->sockFd = socket (s->hp->h_addrtype, SOCK_STREAM, 0); |
| if (s->sockFd < 0) |
| ERROR ("socket"); |
| |
| memset ((void *)&s->sa, 0, sizeof (s->sa)); |
| ASSERT ((s->hp->h_addrtype == AF_INET)); |
| s->sa.sin_family = s->hp->h_addrtype; |
| s->sa.sin_addr.s_addr = htonl (INADDR_ANY); |
| s->sa.sin_port = htons (portNo + p); |
| |
| b = bind (s->sockFd, (struct sockaddr *)&s->sa, sizeof (s->sa)); |
| } |
| while ((b < 0) && (n < NOOFTRIES)); |
| |
| if (b < 0) |
| ERROR ("bind"); |
| |
| s->portNo = portNo + p; |
| #if defined(DEBUGGING) |
| printf ("the receiving host is: %s, the port is %d\n", s->hostname, |
| s->portNo); |
| #endif |
| listen (s->sockFd, 1); |
| return s; |
| } |
| |
| /* tcpServerEstablish returns a tcpServerState containing the relevant |
| information about a socket declared to receive tcp connections. */ |
| |
| extern "C" tcpServerState * |
| EXPORT(tcpServerEstablish) (void) |
| { |
| return EXPORT(tcpServerEstablishPort) (PORTSTART); |
| } |
| |
| /* tcpServerAccept returns a file descriptor once a client has connected and |
| been accepted. */ |
| |
| extern "C" int |
| EXPORT(tcpServerAccept) (tcpServerState *s) |
| { |
| socklen_t i = sizeof (s->isa); |
| int t; |
| |
| #if defined(DEBUGGING) |
| printf ("before accept %d\n", s->sockFd); |
| #endif |
| t = accept (s->sockFd, (struct sockaddr *)&s->isa, &i); |
| return t; |
| } |
| |
| /* tcpServerPortNo returns the portNo from structure, s. */ |
| |
| extern "C" int |
| EXPORT(tcpServerPortNo) (tcpServerState *s) |
| { |
| return s->portNo; |
| } |
| |
| /* tcpServerSocketFd returns the sockFd from structure, s. */ |
| |
| extern "C" int |
| EXPORT(tcpServerSocketFd) (tcpServerState *s) |
| { |
| return s->sockFd; |
| } |
| |
| /* getLocalIP returns the IP address of this machine. */ |
| |
| extern "C" unsigned int |
| EXPORT(getLocalIP) (tcpServerState *s) |
| { |
| char hostname[1024]; |
| struct hostent *hp; |
| struct sockaddr_in sa; |
| unsigned int ip; |
| int ret = gethostname (hostname, sizeof (hostname)); |
| |
| if (ret == -1) |
| { |
| ERROR ("gethostname"); |
| return 0; |
| } |
| |
| hp = gethostbyname (hostname); |
| if (hp == NULL) |
| { |
| ERROR ("gethostbyname"); |
| return 0; |
| } |
| |
| if (sizeof (unsigned int) != sizeof (in_addr_t)) |
| { |
| ERROR ("bad ip length"); |
| return 0; |
| } |
| |
| memset (&sa, 0, sizeof (struct sockaddr_in)); |
| sa.sin_family = AF_INET; |
| sa.sin_port = htons (80); |
| if (hp->h_length == sizeof (unsigned int)) |
| { |
| memcpy (&ip, hp->h_addr_list[0], hp->h_length); |
| return ip; |
| } |
| |
| return 0; |
| } |
| |
| /* tcpServerIP returns the IP address from structure s. */ |
| |
| extern "C" int |
| EXPORT(tcpServerIP) (tcpServerState *s) |
| { |
| return *((int *)s->hp->h_addr_list[0]); |
| } |
| |
| /* tcpServerClientIP returns the IP address of the client who |
| has connected to server s. */ |
| |
| extern "C" unsigned int |
| EXPORT(tcpServerClientIP) (tcpServerState *s) |
| { |
| unsigned int ip; |
| |
| ASSERT (s->isa.sin_family == AF_INET); |
| ASSERT (sizeof (ip) == 4); |
| memcpy (&ip, &s->isa.sin_addr, sizeof (ip)); |
| return ip; |
| } |
| |
| /* tcpServerClientPortNo returns the port number of the client who |
| has connected to server s. */ |
| |
| extern "C" unsigned int |
| EXPORT(tcpServerClientPortNo) (tcpServerState *s) |
| { |
| return s->isa.sin_port; |
| } |
| |
| /* |
| **************************************************************** |
| *** C L I E N T R O U T I N E S |
| **************************************************************** |
| */ |
| |
| typedef struct |
| { |
| char hostname[MAXHOSTNAME]; |
| struct hostent *hp; |
| struct sockaddr_in sa; |
| int sockFd; |
| int portNo; |
| } tcpClientState; |
| |
| /* tcpClientSocket returns a file descriptor (socket) which has |
| connected to, serverName:portNo. */ |
| |
| extern "C" tcpClientState * |
| EXPORT(tcpClientSocket) (char *serverName, int portNo) |
| { |
| tcpClientState *s = (tcpClientState *)malloc (sizeof (tcpClientState)); |
| |
| if (s == NULL) |
| ERROR ("no more memory"); |
| |
| /* Remove SIGPIPE which is raised on the server if the client is killed. */ |
| signal (SIGPIPE, SIG_IGN); |
| |
| s->hp = gethostbyname (serverName); |
| if (s->hp == NULL) |
| { |
| fprintf (stderr, "cannot find host: %s\n", serverName); |
| exit (1); |
| } |
| |
| memset ((void *)&s->sa, 0, sizeof (s->sa)); |
| s->sa.sin_family = AF_INET; |
| memcpy ((void *)&s->sa.sin_addr, (void *)s->hp->h_addr, s->hp->h_length); |
| s->portNo = portNo; |
| s->sa.sin_port = htons (portNo); |
| |
| /* Open a TCP socket (an Internet stream socket). */ |
| |
| s->sockFd = socket (s->hp->h_addrtype, SOCK_STREAM, 0); |
| return s; |
| } |
| |
| /* tcpClientSocketIP returns a file descriptor (socket) which has |
| connected to, ip:portNo. */ |
| |
| extern "C" tcpClientState * |
| EXPORT(tcpClientSocketIP) (unsigned int ip, int portNo) |
| { |
| tcpClientState *s = (tcpClientState *)malloc (sizeof (tcpClientState)); |
| |
| if (s == NULL) |
| ERROR ("no more memory"); |
| |
| /* Remove SIGPIPE which is raised on the server if the client is killed. */ |
| signal (SIGPIPE, SIG_IGN); |
| |
| memset ((void *)&s->sa, 0, sizeof (s->sa)); |
| s->sa.sin_family = AF_INET; |
| memcpy ((void *)&s->sa.sin_addr, (void *)&ip, sizeof (ip)); |
| s->portNo = portNo; |
| s->sa.sin_port = htons (portNo); |
| |
| /* Open a TCP socket (an Internet stream socket). */ |
| |
| s->sockFd = socket (PF_INET, SOCK_STREAM, 0); |
| return s; |
| } |
| |
| /* tcpClientConnect returns the file descriptor associated with s, |
| once a connect has been performed. */ |
| |
| extern "C" int |
| EXPORT(tcpClientConnect) (tcpClientState *s) |
| { |
| if (connect (s->sockFd, (struct sockaddr *)&s->sa, sizeof (s->sa)) < 0) |
| ERROR ("failed to connect to the TCP server"); |
| |
| return s->sockFd; |
| } |
| |
| /* tcpClientPortNo returns the portNo from structure s. */ |
| |
| extern "C" int |
| EXPORT(tcpClientPortNo) (tcpClientState *s) |
| { |
| return s->portNo; |
| } |
| |
| /* tcpClientSocketFd returns the sockFd from structure s. */ |
| |
| extern "C" int |
| EXPORT(tcpClientSocketFd) (tcpClientState *s) |
| { |
| return s->sockFd; |
| } |
| |
| /* tcpClientIP returns the sockFd from structure s. */ |
| |
| extern "C" int |
| EXPORT(tcpClientIP) (tcpClientState *s) |
| { |
| #if defined(DEBUGGING) |
| printf ("client ip = %s\n", inet_ntoa (s->sa.sin_addr.s_addr)); |
| #endif |
| return s->sa.sin_addr.s_addr; |
| } |
| #endif |
| |
| /* GNU Modula-2 link fodder. */ |
| |
| extern "C" void |
| M2EXPORT(init) (int, char *[], char *[]) |
| { |
| } |
| |
| extern "C" void |
| M2EXPORT(finish) (int, char *[], char *[]) |
| { |
| } |
| |
| extern "C" void |
| M2EXPORT(dep) (void) |
| { |
| } |
| |
| extern "C" void __attribute__((__constructor__)) |
| M2EXPORT(ctor) (void) |
| { |
| m2pim_M2RTS_RegisterModule ("sckt", M2LIBNAME, |
| M2EXPORT(init), M2EXPORT(finish), |
| M2EXPORT(dep)); |
| } |