From bf5e1802f467b957f616da1ae939c8a10d8b86ce Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Sat, 29 Oct 2016 16:59:47 +0300 Subject: Initial commit --- CMakeLists.txt | 14 +++++ Makefile.am | 5 ++ README.md | 18 +++++++ client.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 31 +++++++++++ server.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.c | 77 +++++++++++++++++++++++++++ utils.h | 12 +++++ 8 files changed, 454 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Makefile.am create mode 100644 README.md create mode 100644 client.c create mode 100644 configure.ac create mode 100644 server.c create mode 100644 utils.c create mode 100644 utils.h 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include + +#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 */ -- cgit v1.2.3