diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2016-10-29 16:59:47 +0300 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2016-10-30 00:27:20 +0300 |
commit | bf5e1802f467b957f616da1ae939c8a10d8b86ce (patch) | |
tree | c5056e57dd6f2d7a89e146cf8eb5df138aa33088 | |
download | tcp-bf5e1802f467b957f616da1ae939c8a10d8b86ce.tar.gz |
Initial commit
-rw-r--r-- | CMakeLists.txt | 14 | ||||
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | client.c | 135 | ||||
-rw-r--r-- | configure.ac | 31 | ||||
-rw-r--r-- | server.c | 162 | ||||
-rw-r--r-- | utils.c | 77 | ||||
-rw-r--r-- | utils.h | 12 |
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; +} @@ -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); +} @@ -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 */ |