/*
* file: netio.c - TCP layer
* project: BSD Sockets (IPK)
* By Kamil Dudka, xdudka00
* mailto: xdudka00@gmail.com
*/
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <ctype.h>
#include <sys/socket.h>
#include "netio.h"
/*
* TCP I/O private data (initialized by netLineInit ())
*/
struct NetLine {
unsigned defLineLength;
char *pBuff;
TSocket sockfd;
};
static TSocket listenfd;
static volatile sig_atomic_t bTerm = 0;
static inline int readChar (int sockfd, char *);
static inline int writeBlock (int sockfd, const char *, size_t);
/*
* SIGTERM signal handler
*/
void sigTermHandle (int i) {
i = 0;
bTerm = 1;
close (listenfd);
printf ("server: exit (SIGTERM caught)\n");
}
/*
* SIGCHLD signal handler
*/
void sigChldHandle (int i) {
wait (&i);
}
/*
* Start concurent server
* procAddr - CallBack proc for incomming connection
* return only if an error occur
*/
int netListen (unsigned portNumber, unsigned listenQueue, PAcceptProc procAddr) {
// Set signal handling
signal (SIGTERM, sigTermHandle);
signal (SIGCHLD, sigChldHandle);
sigset_t sigMask;
sigemptyset (&sigMask);
sigaddset (&sigMask, SIGTERM);
sigaddset (&sigMask, SIGCHLD);
sigprocmask (SIG_UNBLOCK, &sigMask, NULL);
// Create socket
listenfd = socket (PF_INET, SOCK_STREAM, 0);
if (0> listenfd)
return 1;
// Bind port
struct sockaddr_in sa;
bzero (&sa, sizeof (sa));
sa.sin_family = PF_INET;
sa.sin_addr.s_addr = htonl (INADDR_ANY);
sa.sin_port = htons (portNumber);
if (0> bind (listenfd, (struct sockaddr*) &sa, sizeof (sa))) {
fprintf (stderr, "server: error: Could not bind\n");
close (listenfd);
return 1;
}
// Listen
if (listen (listenfd, listenQueue))
return 1;
// Accept and reply
socklen_t cbClAddr = sizeof (sa);
while (!bTerm) {
int connfd = accept (listenfd, (struct sockaddr*) &sa, &cbClAddr);
if (0> connfd)
break;
if (0== fork ()) {
// Child process
close (listenfd);
// Call call-back procedure and exit
exit (procAddr (connfd));
}
close (connfd);
}
return 0;
}
/*
* Connect to remote server
* Set sockfd in *pSock
* Return 0 if no error occur
*/
int netConnect (const char *serverName, unsigned portNumber, TSocket *pSock) {
// Server address
struct sockaddr_in sa;
bzero (&sa, sizeof (sa));
sa.sin_family = PF_INET;
sa.sin_port = htons (portNumber);
// Deteremine type of addr (IP/URL)
if (isdigit(*serverName)) {
// Direct IP adress
if (0>= inet_pton (PF_INET, serverName, &sa.sin_addr)) {
fprintf (stderr, "client: error: Invalid address\n");
return 1;
}
} else {
// NS-lookup
struct hostent *pHost;
if (NULL== (pHost= gethostbyname (serverName))) {
fprintf (stderr, "client: error: NS-lookup failed\n");
return 1;
}
memcpy (&sa.sin_addr, pHost->h_addr_list[0], pHost->h_length);
}
// Create socket
if (0> (*pSock = socket (PF_INET, SOCK_STREAM, 0))) {
return 1;
}
// Connect to server
if (0!= connect (*pSock, (const struct sockaddr*) &sa, sizeof (sa))) {
fprintf (stderr, "client: error: connect failed\n");
close (*pSock);
return 1;
}
return 0;
}
/*
* Close opened socket
*/
void netClose (TSocket socket) {
close (socket);
}
/*
* Initialize TCP I/O
*/
PNetLine netLineInit (TSocket socket, unsigned defLineLength) {
PNetLine pNL = malloc (sizeof (TNetLine));
if (NULL== pNL)
return NULL;
pNL->pBuff = NULL;
pNL->sockfd = socket;
pNL->defLineLength = defLineLength;
return pNL;
}
/*
* Destroy TCP I/O
*/
void netLineDestroy (PNetLine pNL) {
free (pNL->pBuff);
free (pNL);
}
/*
* Read one line from other side
*/
int netLineGet (PNetLine pNL, char **szText) {
// Initial allocation
int size = pNL->defLineLength;
free (pNL->pBuff);
pNL->pBuff = malloc (size);
// Read one line
int cbWritten=0;
while ( 0<readChar (pNL->sockfd, pNL->pBuff + cbWritten) && pNL->pBuff[cbWritten] != '\n')
if (++cbWritten >= size) {
size <<= 1;
if (NULL== (pNL->pBuff= realloc (pNL->pBuff, size) ))
return -1;
}
if (pNL->pBuff [cbWritten] != '\n')
// Error during reading
return -1;
// End with zero end return string
pNL->pBuff [cbWritten] = '\0';
*szText = pNL->pBuff;
// All ok
return 0;
}
/*
* Send plain text to other side
*/
int netPut (PNetLine pNL, const char *szText) {
if (0> writeBlock (pNL->sockfd, szText, strlen (szText)))
return -1;
return 0;
}
/*
* Read one char from socket
*/
static inline int readChar (int sockfd, char *pTo) {
while (0> read (sockfd, pTo, 1))
if (errno != EINTR)
return -1;
return 1;
}
/*
* Write block to socket
*/
static inline int writeBlock (int sockfd, const char *pBuff, size_t size) {
int cbWritten = 0;
while (size > 0) {
if (0>= (cbWritten= write (sockfd, pBuff, size) )) {
if (cbWritten<0 && errno==EINTR)
cbWritten = 0;
else
return -1;
}
size -= cbWritten;
pBuff += cbWritten;
}
return cbWritten;
}