commit 753c136018bfb1946eab42bed3257a3b12e9e8bd
parent bb74152c4bdcc9a9ea91eb40b0bd0d08e0cfc970
Author: Christos Margiolis <christos@margiolis.net>
Date: Sat, 29 May 2021 21:11:04 +0300
can use the internet domain
Diffstat:
2 files changed, 166 insertions(+), 52 deletions(-)
diff --git a/c_os2/ex2/ex3_client.c b/c_os2/ex2/ex3_client.c
@@ -2,6 +2,10 @@
#include <sys/types.h>
#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
@@ -30,68 +34,119 @@ emalloc(size_t nb)
if ((p = malloc(nb)) == NULL)
err(1, "malloc");
+
return p;
}
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %1$s [-s sockfile]\n"
+ " %1$s [-i [-p port]] [-s sockfile] hostname\n"
+ " %1$s [-i [-p port]] [-s sockfile] ipv4_addr\n", argv0);
+ exit(1);
+}
+
int
main(int argc, char *argv[])
{
struct pack_res *res;
struct sockaddr_un sun;
+ struct sockaddr_in sin;
+ struct hostent *hp;
char *sockfile = "/tmp/cool.sock";
int *arr;
- int fd, i, n;
+ int fd, i, n, rc;
+ int port = 9999;
+ int iflag, uflag;
char ch;
argv0 = *argv;
- while ((ch = getopt(argc, argv, "s:")) != -1) {
+ /* Run on the UNIX domain by default. */
+ uflag = 1;
+ iflag = 0;
+
+ while ((ch = getopt(argc, argv, "ip:s:")) != -1) {
switch (ch) {
+ case 'i':
+ /* Run the server on the internet domain. */
+ iflag = 1;
+ uflag = 0;
+ break;
+ case 'p':
+ /* Choose custom port but don't use a well-known port. */
+ if ((port = atoi(optarg)) < 1024)
+ errx(1, "can't use port number < 1024");
+ break;
case 's':
sockfile = optarg;
break;
case '?':
default:
- fprintf(stderr, "usage: %s [-s sockfile]\n", argv0);
- break;
+ usage();
}
}
argc -= optind;
argv += optind;
-
- if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
- err(1, "socket");
- (void)memset(&sun, 0, sizeof(sun));
- sun.sun_family = AF_UNIX;
- (void)strncpy(sun.sun_path, sockfile, sizeof(sun.sun_path) - 1);
- if (connect(fd, (struct sockaddr *)&sun, sizeof(struct sockaddr_un)) < 0)
- err(1, "connect");
+ /* If we're on the internet domain, we also need a hostname. */
+ if (iflag && argc < 1)
+ usage();
+
+ if (iflag) {
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ err(1, "socket(AF_INET)");
+ (void)memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ if (!inet_aton(*argv, &sin.sin_addr)) {
+ if ((hp = gethostbyname(*argv)) == NULL)
+ errx(1, "gethostbyname(%s) failed", *argv);
+ (void)memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
+ }
+ if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(1, "connect");
+ } else if (uflag) {
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ err(1, "socket(AF_UNIX)");
+ (void)memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ (void)strncpy(sun.sun_path, sockfile, sizeof(sun.sun_path) - 1);
+
+ if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
+ err(1, "connect");
+ }
res = emalloc(sizeof(struct pack_res));
for (;;) {
-
/* Remove any previous junk. */
(void)memset(res, 0, sizeof(struct pack_res));
- printf("%s> n: ", argv0);
- scanf("%d", &n);
- /* Flush buffer */
- (void)getchar();
+ /* Make sure we send valid input to the server */
+ do {
+ printf("%s> n: ", argv0);
+ rc = scanf("%d", &n);
+ /* Flush input buffer */
+ (void)getchar();
+ } while (rc != 1);
arr = emalloc(n * sizeof(int));
for (i = 0; i < n; i++) {
- printf("%s> arr[%d]: ", argv0, i);
- scanf("%d", &arr[i]);
+ do {
+ printf("%s> arr[%d]: ", argv0, i);
+ rc = scanf("%d", &arr[i]);
+ (void)getchar();
+ } while (rc != 1);
}
- (void)getchar();
if (send(fd, &n, sizeof(int), 0) < 0)
err(1, "send");
if (send(fd, arr, n * sizeof(int), 0) < 0)
err(1, "send");
if (recv(fd, res, sizeof(struct pack_res), 0) < 0)
err(1, "recv");
- printf("response: %s\tavg: %.2f\n", res->str, res->avg);
+ free(arr);
+ printf("server response: %s\tavg: %.2f\n", res->str, res->avg);
printf("%s> continue (y/n)? ", argv0);
ch = getchar();
if (send(fd, &ch, 1, 0) < 0)
diff --git a/c_os2/ex2/ex3_server.c b/c_os2/ex2/ex3_server.c
@@ -2,6 +2,10 @@
#include <sys/types.h>
#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
#include <err.h>
#include <signal.h>
#include <stdio.h>
@@ -33,7 +37,7 @@ static void sighandler(int);
static void *emalloc(size_t);
static char *argv0;
-/* Only gets set if a termination signal is caught. */
+/* Gets set only if a termination signal is caught. */
static volatile sig_atomic_t f_quit = 0;
static int
@@ -46,6 +50,7 @@ srv(struct foo *foo)
int rc;
char cont;
+ f = (struct foo *)foo;
/*
* The return code is -1 (error) by default so that we don't
* need to set it everytime an error occurs in the main loop.
@@ -53,7 +58,6 @@ srv(struct foo *foo)
* after the loop.
*/
rc = -1;
- f = (struct foo *)foo;
for (;;) {
if (recv(f->cfd, &n, sizeof(int), 0) < 0)
goto fail;
@@ -61,6 +65,7 @@ srv(struct foo *foo)
if (recv(f->cfd, arr, n * sizeof(int), 0) < 0)
goto fail;
res = emalloc(sizeof(struct pack_res));
+
printf("cfd: %d\tn: %d\n", f->cfd, n);
for (i = 0, sum = 0; i < n; i++) {
printf("cfd: %d\tarr[%d]: %d\n", f->cfd, i, arr[i]);
@@ -68,7 +73,7 @@ srv(struct foo *foo)
}
free(arr);
/*
- * If we go to `fail:`, `gcc` for some reason double frees if
+ * When we go to `fail:`, `gcc` for some reason double frees if
* we don't manually set it to NULL, even though we explicitly
* check for NULL first...
*/
@@ -118,13 +123,16 @@ emalloc(size_t nb)
if ((p = malloc(nb)) == NULL)
err(1, "malloc");
+
return p;
}
static void
usage(void)
{
- fprintf(stderr, "usage: %s [-b backlog] [-s sockfile]\n", argv0);
+ fprintf(stderr, "usage: %1$s [-b backlog] [-s sockfile]\n"
+ " %1$s [-i [-p port]] [-b backlog] [-s sockfile] hostname\n"
+ " %1$s [-i [-p port]] [-b backlog] [-s sockfile] ipv4_addr\n", argv0);
exit(1);
}
@@ -133,14 +141,22 @@ main(int argc, char *argv[])
{
struct foo *f;
struct sockaddr_un sun;
+ struct sockaddr_in sin;
+ struct hostent *hp;
struct sigaction sa;
char *sockfile = "/tmp/cool.sock";
int sfd;
int backlog = 10;
+ int port = 9999;
+ int iflag, uflag, sockflags;
char ch;
argv0 = *argv;
- if ((ch = getopt(argc, argv, "b:s:")) != -1) {
+ /* Run on the UNIX domain by default. */
+ uflag = 1;
+ iflag = 0;
+
+ while ((ch = getopt(argc, argv, "b:ip:s:")) != -1) {
switch (ch) {
case 'b':
/*
@@ -154,6 +170,16 @@ main(int argc, char *argv[])
if ((backlog = atoi(optarg)) < 1)
usage();
break;
+ case 'i':
+ /* Run the server on the internet domain. */
+ iflag = 1;
+ uflag = 0;
+ break;
+ case 'p':
+ /* Choose custom port but don't use a well-known port. */
+ if ((port = atoi(optarg)) < 1024)
+ errx(1, "can't use port number < 1024");
+ break;
case 's':
sockfile = optarg;
break;
@@ -165,30 +191,65 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
+ /*
+ * If we're on the internet domain, we also need a hostname
+ * or an IPv4 address.
+ */
+ if (iflag && argc < 1)
+ usage();
/*
* Handle termination signals so we don't exit abnormally
* (i.e without cleaning up resources).
*/
(void)memset(&sa, 0, sizeof(sa));
- (void)sigemptyset(&sa.sa_mask);
+ (void)sigfillset(&sa.sa_mask);
sa.sa_handler = sighandler;
- sa.sa_flags = SA_RESTART;
- /* FIXME*/
- /*if (sigaction(SIGHUP, &sa, NULL) < 0)*/
- /*err(1, "sigaction: SIGHUP");*/
- /*if (sigaction(SIGINT, &sa, NULL) < 0)*/
- /*err(1, "sigaction: SIGINT");*/
- /*if (sigaction(SIGTERM, &sa, NULL) < 0)*/
- /*err(1, "sigaction: SIGTERM");*/
-
- if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
- err(1, "socket");
- (void)memset(&sun, 0, sizeof(sun));
- sun.sun_family = AF_UNIX;
- (void)strncpy(sun.sun_path, sockfile, sizeof(sun.sun_path) - 1);
-
- if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
- err(1, "bind");
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ err(1, "sigaction(SIGINT)");
+ if (sigaction(SIGTERM, &sa, NULL) < 0)
+ err(1, "sigaction(SIGTERM)");
+
+ /* Set up the socket for use in the Internet domain. */
+ if (iflag) {
+ if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ err(1, "socket(AF_INET)");
+ (void)memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ /* Convert the port number to network bytes. */
+ sin.sin_port = htons(port);
+ /*
+ * We'll try and see if the input is an IPv4 address. If
+ * inet_aton(3) does not fail, the user passed an IPv4 address.
+ * However if inet_addr(3) fail, we'll assume the user passed
+ * an hostname. That lets us use both hostnames and IPv4
+ * addresses as arguments.
+ */
+ if (!inet_aton(*argv, &sin.sin_addr)) {
+ /* Get host info */
+ if ((hp = gethostbyname(*argv)) == NULL)
+ errx(1, "gethostbyname(%s) failed", *argv);
+ /* `hp->h_addr` has the host's IPv4 address. */
+ (void)memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
+ }
+ if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(1, "bind");
+
+ printf("Socket: %s\nDomain: AF_INET\nIPv4: %s\n"
+ "Port: %d\nBacklog: %d\n",
+ sockfile, inet_ntoa(sin.sin_addr), port, backlog);
+ /* Set up the socket for use in the UNIX domain. */
+ } else if (uflag) {
+ if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ err(1, "socket(AF_UNIX)");
+ (void)memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ (void)strncpy(sun.sun_path, sockfile, sizeof(sun.sun_path) - 1);
+ if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
+ err(1, "bind");
+
+ printf("Socket: %s\nDomain: AF_UNIX\nBacklog: %d\n",
+ sockfile, backlog);
+ }
if (listen(sfd, backlog) < 0)
err(1, "listen");
@@ -197,7 +258,7 @@ main(int argc, char *argv[])
f->ntotal = 0;
for (;;) {
- /* FIXME: blocked by accept(2) */
+ /* We caught a termination signal. */
if (f_quit)
break;
/*
@@ -207,6 +268,10 @@ main(int argc, char *argv[])
if ((f->cfd = accept(sfd, NULL, NULL)) < 0)
continue;
printf("[%s] accepted client: %d\n", argv0, f->cfd);
+ /*
+ * Create a child process to serve the client so the parent can
+ * continue waiting for another client to serve.
+ */
switch (fork()) {
case -1:
err(1, "fork");
@@ -219,17 +284,11 @@ main(int argc, char *argv[])
}
}
-
/* Will get here only if a termination signal is caught. */
(void)close(f->cfd);
(void)close(sfd);
- free(f);
- /*
- * bind(2)'s man page states that the socket should be deleted when
- * it's no longer needed, otherwise it'll stay there even after
- * we exit.
- */
(void)unlink(sockfile);
+ free(f);
return 0;
}