aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2016-10-29 16:59:47 +0300
committerIgor Pashev <pashev.igor@gmail.com>2016-10-30 00:27:20 +0300
commitbf5e1802f467b957f616da1ae939c8a10d8b86ce (patch)
treec5056e57dd6f2d7a89e146cf8eb5df138aa33088
downloadtcp-bf5e1802f467b957f616da1ae939c8a10d8b86ce.tar.gz
Initial commit
-rw-r--r--CMakeLists.txt14
-rw-r--r--Makefile.am5
-rw-r--r--README.md18
-rw-r--r--client.c135
-rw-r--r--configure.ac31
-rw-r--r--server.c162
-rw-r--r--utils.c77
-rw-r--r--utils.h12
8 files changed, 454 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..37185b2
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,14 @@
+CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
+PROJECT (FileSender)
+
+INCLUDE (CheckIncludeFiles)
+CHECK_INCLUDE_FILES (sys/sendfile.h HAVE_SYS_SENDFILE_H)
+
+INCLUDE (CheckFunctionExists)
+CHECK_FUNCTION_EXISTS (socket HAVE_SOCKET)
+IF(NOT HAVE_SOCKET)
+ CHECK_LIBRARY_EXISTS (socket socket "" HAVE_SOCKET)
+ENDIF(NOT HAVE_SOCKET)
+
+ADD_EXECUTABLE (server server.c utils.c utils.h)
+ADD_EXECUTABLE (client client.c utils.c utils.h)
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..5afa0c9
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,5 @@
+bin_PROGRAMS = server client
+
+server_SOURCES = server.c utils.c utils.h
+client_SOURCES = client.c utils.c utils.h
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..298fc4e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# TCP toy project
+
+```
+# ./server
+./server: accepting connections on port 1234
+./server: will save files under `/var/tmp'
+./server: received 1165 of `fstab'
+
+```
+
+```
+# ./client localhost:1234 /etc/fstab
+./client: connecting to host localhost, port 1234
+./client: sending `/etc/fstab'
+./client: sent 1165 bytes of `/etc/fstab'
+
+```
+
diff --git a/client.c b/client.c
new file mode 100644
index 0000000..161284b
--- /dev/null
+++ b/client.c
@@ -0,0 +1,135 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sendfile.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+const char *progname = NULL;
+char *port = "1234";
+
+static void
+usage ()
+{
+ printf ("%s: send file to remote host\n", progname);
+ printf ("Usage: %s host[:port] file\n", progname);
+ printf ("Default port is %s\n", port);
+}
+
+static void
+send_file (int fd, const char *filename)
+{
+ off_t dummy = 0; /* specially for Solaris (it crashes if sendfile's 3rd arg is NULL). */
+ int sfd = open (filename, O_RDONLY);
+ if (sfd < 0)
+ {
+ fatal ("cannot read `%s'", filename);
+ }
+
+ struct stat st;
+ if (!fstat (sfd, &st))
+ {
+ ssize_t rc;
+ info ("sending `%s'", filename);
+ const char *name = basename ((char *) filename);
+ size_t filename_len = strlen (name) + 1;
+ rc = write (fd, name, filename_len); /* filename\0payload */
+ if (rc != filename_len)
+ {
+ fatal ("failed to write filename");
+ }
+ rc = sendfile (fd, sfd, &dummy, st.st_size);
+ if (rc < 0)
+ {
+ fatal ("failed to send file `%s'", filename);
+ }
+ else if (rc != st.st_size)
+ {
+ warning ("sent only %d of %d bytes of file `%s'", rc,
+ st.st_size, filename);
+ }
+ else
+ {
+ info ("sent %d bytes of `%s'", rc, filename);
+ }
+ }
+ else
+ {
+ fatal ("failed to stat `%s'", filename);
+ }
+
+ (void) close (sfd);
+}
+
+int
+main (int argc, char **argv)
+{
+ const char *host = NULL;
+ char *colon = NULL;
+ ssize_t rc;
+ int fd;
+ struct addrinfo hints;
+ struct addrinfo *result;
+ struct addrinfo *rp = NULL;
+
+ progname = argv[0];
+ if (argc != 3)
+ {
+ usage ();
+ exit (EXIT_FAILURE);
+ }
+
+ host = argv[1];
+ colon = strchr (argv[1], ':');
+ if (colon)
+ {
+ *colon = '\0';
+ port = colon + 1;
+ }
+
+ info ("connecting to host %s, port %s", host, port);
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ rc = getaddrinfo (host, port, &hints, &result);
+ if (0 != rc)
+ {
+ fatal ("getaddrinfo() failed");
+ }
+ for (rp = result; rp != NULL; rp = rp->ai_next)
+ {
+ fd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (fd == -1)
+ continue;
+
+ if (!connect (fd, rp->ai_addr, rp->ai_addrlen))
+ break;
+
+ (void) close (fd);
+ }
+ freeaddrinfo (result);
+
+ if (rp == NULL)
+ {
+ fatal ("failed to connect");
+ }
+
+ send_file (fd, argv[2]);
+
+ return EXIT_SUCCESS;
+}
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..c918a02
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,31 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.69])
+AC_INIT([File Sender], [0.0.1], [pashev.igor@gmai.com])
+AM_INIT_AUTOMAKE([dist-xz foreign])
+AC_CONFIG_SRCDIR([server.c])
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
+AC_PROG_CC_C99
+AC_USE_SYSTEM_EXTENSIONS
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_CHECK_HEADERS([fcntl.h netinet/in.h stddef.h stdlib.h string.h sys/socket.h unistd.h sys/sendfile.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_CHECK_TYPES([ptrdiff_t])
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([memset strerror])
+AC_SEARCH_LIBS([socket], [socket])
+AC_SEARCH_LIBS([sendfile], [sendfile])
+
+AC_OUTPUT([Makefile])
+
diff --git a/server.c b/server.c
new file mode 100644
index 0000000..b383071
--- /dev/null
+++ b/server.c
@@ -0,0 +1,162 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+const char *progname = NULL;
+int port = 1234;
+int listen_queue = 100;
+const char *basedir = "/var/tmp";
+
+static void
+recvfile (int fd)
+{
+ ssize_t rc;
+ int dfd = -1;
+ char *filename = NULL;
+ size_t filename_len;
+ off_t filelength = 0;
+ char *buf = malloc (PATH_MAX);
+ if (!buf)
+ {
+ warning ("failed to allocate space");
+ goto clean;
+ }
+
+ rc = read (fd, buf, PATH_MAX); /* filename\0payload */
+ if (rc < 0)
+ {
+ warning ("failed to read filename");
+ goto clean;
+ }
+ else if (0 == rc)
+ {
+ info ("no data received, connection closed by client");
+ goto clean;
+ }
+
+ /* XXX no subdirs. */
+ filename = strndup (buf, PATH_MAX);
+ if (!filename)
+ {
+ warning ("failed to create filename");
+ goto clean;
+ }
+ filename_len = strlen (filename);
+
+ dfd =
+ openat (AT_FDCWD, filename, O_CREAT + O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP);
+ if (dfd < 0)
+ {
+ warning ("failed to open file `%s'", filename);
+ goto clean;
+ }
+
+ char *payload_start = buf + filename_len + 1;
+ ssize_t rest_len = rc - filename_len - 1;
+ rc = write (dfd, payload_start, rest_len); /* write the rest of buf. */
+ if (rc != rest_len)
+ {
+ warning ("Failed to write `%s'", filename);
+ goto clean;
+ }
+ filelength += rc;
+
+ while ((rc = read (fd, buf, PATH_MAX)) > 0)
+ {
+ if (write (dfd, buf, rc) < 0)
+ {
+ warning ("failed to write `%s'", filename);
+ goto clean;
+ }
+ filelength += rc;
+ }
+ info ("received %lu of `%s'", filelength, filename);
+
+clean:
+ if (buf)
+ free (buf);
+ if (filename)
+ free (filename);
+ if (dfd > 0)
+ {
+ if (close (dfd))
+ {
+ warning ("failed to close %d", dfd);
+ }
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ NOTUSED (argc);
+
+ int listen_fd;
+ int conn_fd;
+ struct sockaddr_in servaddr;
+ int rc;
+
+ progname = argv[0];
+
+ if (chdir (basedir))
+ {
+ fatal ("failed to change directory to `%s'", basedir);
+ }
+
+ listen_fd = socket (AF_INET, SOCK_STREAM, 0);
+ if (listen_fd < 0)
+ {
+ fatal ("failed to create listen socket");
+ }
+
+ memset (&servaddr, 0, sizeof (servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
+ servaddr.sin_port = htons (port);
+
+ rc = bind (listen_fd, (const struct sockaddr *) &servaddr,
+ sizeof (servaddr));
+ if (0 != rc)
+ {
+ fatal ("failed to bind listen socket");
+ }
+
+ rc = listen (listen_fd, listen_queue);
+ if (0 != rc)
+ {
+ fatal ("failed to plug listen socket");
+ }
+
+ info ("accepting connections on port %d", port);
+ info ("will save files under `%s'", basedir);
+ while (1)
+ {
+ conn_fd = accept (listen_fd, NULL, NULL);
+ if (conn_fd < 0)
+ {
+ warning ("accept() failed");
+ continue;
+ }
+ recvfile (conn_fd);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/utils.c b/utils.c
new file mode 100644
index 0000000..084d795
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,77 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+static void
+info_va (const char *msg, va_list va)
+{
+ fprintf (stderr, "%s: ", progname);
+ vfprintf (stderr, msg, va);
+ fputs ("", stderr);
+}
+
+void
+info (const char *msg, ...)
+{
+ va_list ap;
+
+ va_start (ap, msg);
+ info_va (msg, ap);
+ va_end (ap);
+}
+
+static void
+warning_va (const char *msg, va_list va)
+{
+ fprintf (stderr, "%s: WARNING: ", progname);
+ vfprintf (stderr, msg, va);
+ if (errno)
+ {
+ fprintf (stderr, ": %s", strerror (errno));
+ }
+ fputs ("", stderr);
+}
+
+void
+warning (const char *msg, ...)
+{
+ va_list ap;
+
+ va_start (ap, msg);
+ warning_va (msg, ap);
+ va_end (ap);
+}
+
+static void
+fatal_va (const char *msg, va_list va)
+{
+ fprintf (stderr, "%s: FATAL: ", progname);
+ vfprintf (stderr, msg, va);
+ if (errno)
+ {
+ fprintf (stderr, ": %s", strerror (errno));
+ }
+ fputs ("", stderr);
+}
+
+void
+fatal (const char *msg, ...)
+{
+ va_list ap;
+
+ va_start (ap, msg);
+ fatal_va (msg, ap);
+ va_end (ap);
+ exit (EXIT_FAILURE);
+}
diff --git a/utils.h b/utils.h
new file mode 100644
index 0000000..8a19473
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,12 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+extern const char *progname;
+
+void info (const char *, ...);
+void warning (const char *, ...);
+void fatal (const char *, ...);
+
+#define NOTUSED(x) ((void)(x))
+
+#endif /* UTILS_H */