158 lines
4.8 KiB
C
158 lines
4.8 KiB
C
/*
|
|
* Support functions for dealing with sockets.
|
|
*
|
|
* Note: these functions cannot be used out of the box.
|
|
* In particular, support for protocol independent programming
|
|
* is not fully implemented. See below.
|
|
*
|
|
* Written by G. Back for CS 3214 Spring 2018.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sendfile.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "socket.h"
|
|
#include "main.h"
|
|
|
|
/*
|
|
* Find a suitable IPv4 address to bind to, create a socket, bind it,
|
|
* invoke listen to get the socket ready for accepting clients.
|
|
*
|
|
* This function does not implement proper support for protocol-independent/
|
|
* dual-stack binding. Adding this is part of the assignment.
|
|
*
|
|
* Returns -1 on error, setting errno.
|
|
* Returns socket file descriptor otherwise.
|
|
*/
|
|
int
|
|
socket_open_bind_listen(char * port_number_string, int backlog)
|
|
{
|
|
struct addrinfo *info, *pinfo;
|
|
struct addrinfo hint;
|
|
|
|
memset(&hint, 0, sizeof hint);
|
|
|
|
hint.ai_flags = AI_PASSIVE | // we're looking for an address to bind to
|
|
AI_NUMERICSERV; // service port is numeric, don't look in /etc/services
|
|
|
|
hint.ai_protocol = IPPROTO_TCP; // only interested in TCP
|
|
int rc = getaddrinfo(NULL, port_number_string, &hint, &info);
|
|
if (rc != 0) {
|
|
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(rc));
|
|
return -1;
|
|
}
|
|
|
|
char printed_addr[1024];
|
|
for (pinfo = info; pinfo; pinfo = pinfo->ai_next) {
|
|
assert (pinfo->ai_protocol == IPPROTO_TCP);
|
|
int rc = getnameinfo(pinfo->ai_addr, pinfo->ai_addrlen,
|
|
printed_addr, sizeof printed_addr, NULL, 0,
|
|
NI_NUMERICHOST);
|
|
if (rc != 0) {
|
|
fprintf(stderr, "getnameinfo error: %s\n", gai_strerror(rc));
|
|
return -1;
|
|
}
|
|
|
|
/* Uncomment this to see the address returned
|
|
printf("%s: %s\n", pinfo->ai_family == AF_INET ? "AF_INET" :
|
|
pinfo->ai_family == AF_INET6 ? "AF_INET6" : "?",
|
|
printed_addr);
|
|
*/
|
|
|
|
/* Skip any non-IPv4 addresses.
|
|
* Adding support for protocol independence/IPv6 is part of the project.
|
|
*/
|
|
if (pinfo->ai_family != AF_INET)
|
|
continue;
|
|
|
|
int s = socket(pinfo->ai_family, pinfo->ai_socktype, pinfo->ai_protocol);
|
|
if (s == -1) {
|
|
perror("socket");
|
|
return -1;
|
|
}
|
|
|
|
// See https://stackoverflow.com/a/3233022 for a good explanation of what this does
|
|
int opt = 1;
|
|
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
|
|
|
|
rc = bind(s, pinfo->ai_addr, pinfo->ai_addrlen);
|
|
if (rc == -1) {
|
|
perror("bind");
|
|
close(s);
|
|
return -1;
|
|
}
|
|
|
|
rc = listen(s, backlog);
|
|
if (rc == -1) {
|
|
perror("listen");
|
|
close(s);
|
|
return -1;
|
|
}
|
|
|
|
freeaddrinfo(info);
|
|
return s;
|
|
}
|
|
fprintf(stderr, "No suitable address to bind port %s found\n", port_number_string);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Accept a client, blocking if necessary.
|
|
*
|
|
* Returns file descriptor of client accepted on success, returns
|
|
* -1 on error.
|
|
*/
|
|
int
|
|
socket_accept_client(int accepting_socket)
|
|
{
|
|
/* The address passed into accept must be large enough for either IPv4 & IPv6.
|
|
* Using a struct sockaddr is too small to hold a full IPv6 address and accept()
|
|
* would not return the full address.
|
|
*/
|
|
struct sockaddr_storage peer;
|
|
socklen_t peersize = sizeof(peer);
|
|
|
|
int client = accept(accepting_socket, (struct sockaddr *) &peer, &peersize);
|
|
if (client == -1) {
|
|
perror("accept");
|
|
return -1;
|
|
}
|
|
|
|
/* Performance tuning. Turn off Nagle's algorithm.
|
|
* Otherwise, when the servers sends a reply to the client, and the reply
|
|
* is small relative to the MSS size, there would be a delay of 40ms
|
|
* during which the OS would hope in vain for more data to be sent.
|
|
* See tcp(7)
|
|
*/
|
|
int i = 1;
|
|
setsockopt(client, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i));
|
|
|
|
/* The following will help with debugging your server.
|
|
* Adjust and/or remove as you see fit.
|
|
*/
|
|
if (!silent_mode) {
|
|
char peer_addr[1024], peer_port[10];
|
|
int rc = getnameinfo((struct sockaddr *) &peer, peersize,
|
|
peer_addr, sizeof peer_addr, peer_port, sizeof peer_port,
|
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
|
if (rc != 0) {
|
|
fprintf(stderr, "getnameinfo error: %s\n", gai_strerror(rc));
|
|
return -1;
|
|
}
|
|
fprintf(stderr, "Accepted connection from %s:%s\n", peer_addr, peer_port);
|
|
}
|
|
return client;
|
|
}
|
|
|