From 65cd70a85bb58e10fa223a4d4f496b31c096e20b Mon Sep 17 00:00:00 2001 From: mpc <mpc> Date: Sun, 20 Jun 2004 11:44:53 +0000 Subject: [PATCH] LibSAM --- apps/sam/c/LICENSE | 27 + apps/sam/c/Makefile.cygwin | 65 ++ apps/sam/c/Makefile.debian | 65 ++ apps/sam/c/Makefile.linux | 64 ++ apps/sam/c/Makefile.mingw | 64 ++ apps/sam/c/doc/donate.txt | 7 + apps/sam/c/doc/license.txt | 27 + apps/sam/c/doc/todo.txt | 9 + apps/sam/c/doc/whatsnew.txt | 26 + apps/sam/c/examples/build.bat | 8 + apps/sam/c/examples/build.sh | 8 + apps/sam/c/examples/dgram-client.c | 125 ++++ apps/sam/c/examples/dgram-server.c | 109 +++ apps/sam/c/inc/platform.h | 140 ++++ apps/sam/c/inc/sam.h | 123 ++++ apps/sam/c/inc/snprintf.h | 49 ++ apps/sam/c/inc/strl.h | 47 ++ apps/sam/c/src/sam.c | 1047 ++++++++++++++++++++++++++++ apps/sam/c/src/snprintf.c | 851 ++++++++++++++++++++++ apps/sam/c/src/strl.c | 84 +++ 20 files changed, 2945 insertions(+) create mode 100644 apps/sam/c/LICENSE create mode 100644 apps/sam/c/Makefile.cygwin create mode 100644 apps/sam/c/Makefile.debian create mode 100644 apps/sam/c/Makefile.linux create mode 100644 apps/sam/c/Makefile.mingw create mode 100644 apps/sam/c/doc/donate.txt create mode 100644 apps/sam/c/doc/license.txt create mode 100644 apps/sam/c/doc/todo.txt create mode 100644 apps/sam/c/doc/whatsnew.txt create mode 100644 apps/sam/c/examples/build.bat create mode 100644 apps/sam/c/examples/build.sh create mode 100644 apps/sam/c/examples/dgram-client.c create mode 100644 apps/sam/c/examples/dgram-server.c create mode 100644 apps/sam/c/inc/platform.h create mode 100644 apps/sam/c/inc/sam.h create mode 100644 apps/sam/c/inc/snprintf.h create mode 100644 apps/sam/c/inc/strl.h create mode 100644 apps/sam/c/src/sam.c create mode 100644 apps/sam/c/src/snprintf.c create mode 100644 apps/sam/c/src/strl.c diff --git a/apps/sam/c/LICENSE b/apps/sam/c/LICENSE new file mode 100644 index 0000000000..ed1daacf69 --- /dev/null +++ b/apps/sam/c/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of any contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/apps/sam/c/Makefile.cygwin b/apps/sam/c/Makefile.cygwin new file mode 100644 index 0000000000..46930684e3 --- /dev/null +++ b/apps/sam/c/Makefile.cygwin @@ -0,0 +1,65 @@ +# +# This Makefile is compatible with GNU Make and should work on Cygwin +# + +# +# Your operating system +# + +OS = CYGWIN + +# +# Directories +# + +INCDIR = inc +LIBDIR = lib +OBJDIR = obj +SRCDIR = src + +# +# Programs +# + +AR = ar +CC = gcc + +# +# Flags +# + +CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall +CFLAGS += -DOS=$(OS) +CFLAGS += -I$(INCDIR) + +# +# Object files +# + +OBJS = $(OBJDIR)/sam.o \ + $(OBJDIR)/snprintf.o \ + $(OBJDIR)/strl.o + +# +# Build rules +# + +all: depend libsam + +depend: + $(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend + +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) -o $@ -c $< + +libsam: $(OBJS) + $(AR) rcs $(LIBDIR)/libsam.a $(OBJS) + +# +# Cleanup rules +# + +clean: + -rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend + +tidy: clean diff --git a/apps/sam/c/Makefile.debian b/apps/sam/c/Makefile.debian new file mode 100644 index 0000000000..8f7b9f3901 --- /dev/null +++ b/apps/sam/c/Makefile.debian @@ -0,0 +1,65 @@ +# +# This Makefile is compatible with GNU Make and should work on Linux +# (Tested on Debian 3.0) +# + +# +# Your operating system +# + +OS = LINUX + +# +# Directories +# + +INCDIR = inc +LIBDIR = lib +OBJDIR = obj +SRCDIR = src + +# +# Programs +# + +AR = ar +CC = gcc-3.0 + +# +# Flags +# + +CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall +CFLAGS += -DOS=$(OS) +CFLAGS += -I$(INCDIR) + +# +# Object files +# + +OBJS = $(OBJDIR)/sam.o \ + $(OBJDIR)/strl.o + +# +# Build rules +# + +all: depend libsam + +depend: + $(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend + +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) -o $@ -c $< + +libsam: $(OBJS) + $(AR) rcs $(LIBDIR)/libsam.a $(OBJS) + +# +# Cleanup rules +# + +clean: + -rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend + +tidy: clean diff --git a/apps/sam/c/Makefile.linux b/apps/sam/c/Makefile.linux new file mode 100644 index 0000000000..a3e5b779bf --- /dev/null +++ b/apps/sam/c/Makefile.linux @@ -0,0 +1,64 @@ +# +# This Makefile is compatible with GNU Make and should work on Linux (generic) +# + +# +# Your operating system +# + +OS = LINUX + +# +# Directories +# + +INCDIR = inc +LIBDIR = lib +OBJDIR = obj +SRCDIR = src + +# +# Programs +# + +AR = ar +CC = gcc + +# +# Flags +# + +CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall +CFLAGS += -DOS=$(OS) +CFLAGS += -I$(INCDIR) + +# +# Object files +# + +OBJS = $(OBJDIR)/sam.o \ + $(OBJDIR)/strl.o + +# +# Build rules +# + +all: depend libsam + +depend: + $(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend + +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) -o $@ -c $< + +libsam: $(OBJS) + $(AR) rcs $(LIBDIR)/libsam.a $(OBJS) + +# +# Cleanup rules +# + +clean: + -rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend + +tidy: clean diff --git a/apps/sam/c/Makefile.mingw b/apps/sam/c/Makefile.mingw new file mode 100644 index 0000000000..4d0a4e571a --- /dev/null +++ b/apps/sam/c/Makefile.mingw @@ -0,0 +1,64 @@ +# +# This Makefile is compatible with GNU Make and should work on Windows (MingW) +# + +# +# Your operating system +# + +OS = MINGW + +# +# Directories +# + +INCDIR = inc +LIBDIR = lib +OBJDIR = obj +SRCDIR = src + +# +# Programs +# + +AR = C:\Dev-Cpp\bin\ar +CC = C:\Dev-Cpp\bin\gcc + +# +# Flags +# + +CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall +CFLAGS += -DOS=$(OS) +CFLAGS += -I$(INCDIR) + +# +# Object files +# + +OBJS = $(OBJDIR)/sam.o \ + $(OBJDIR)/strl.o + +# +# Build rules +# + +all: depend libsam + +depend: + $(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend + +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) -o $@ -c $< + +libsam: $(OBJS) + $(AR) rcs $(LIBDIR)/libsam.a $(OBJS) + +# +# Cleanup rules +# + +clean: + -rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend + +tidy: clean diff --git a/apps/sam/c/doc/donate.txt b/apps/sam/c/doc/donate.txt new file mode 100644 index 0000000000..b74604b67e --- /dev/null +++ b/apps/sam/c/doc/donate.txt @@ -0,0 +1,7 @@ +If you would like to make a donation to the author of this library, you can use +the following methods: + + * E-Gold account number 1043280 + * Paypal email mpc@innographx.com + +If you want to use some other method, just ask. diff --git a/apps/sam/c/doc/license.txt b/apps/sam/c/doc/license.txt new file mode 100644 index 0000000000..ed1daacf69 --- /dev/null +++ b/apps/sam/c/doc/license.txt @@ -0,0 +1,27 @@ +Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of any contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/apps/sam/c/doc/todo.txt b/apps/sam/c/doc/todo.txt new file mode 100644 index 0000000000..6dc2bef8f5 --- /dev/null +++ b/apps/sam/c/doc/todo.txt @@ -0,0 +1,9 @@ +I need to do these things: + +* SAM raw support +* Write an instruction manual + +Anyone can help with these things: + +* Fix the example dgram-client.c +* Compile on as many platforms as possible diff --git a/apps/sam/c/doc/whatsnew.txt b/apps/sam/c/doc/whatsnew.txt new file mode 100644 index 0000000000..5b86737ca0 --- /dev/null +++ b/apps/sam/c/doc/whatsnew.txt @@ -0,0 +1,26 @@ +v1.15 2004-06- + * Fixed some fatal bugs in datagram handling + * Added another error return type named SAM_TOO_BIG - some functions now + return samerr_t instead of bool now + +v1.10 2004-06-16 + * Changed sam_strerror() to work the same as the standard library strerror() + * Ported to native MS Windows (uses the Mingw development environment) + * Fixed a probable bug in the Cygwin port + +v1.05 2004-06-09 + * Added an example datagram client/server program in the examples directory + * sam_read_buffer() now returns bool true if it read anything + * Added repliable datagram support - sam_connect() now has another argument + * Replaced strstr() with the more appropriate strncmp() in many places + * Fixed a parsing error for STREAM CLOSED + * Removed the old sam_naming_lookup() and renamed sam_naming_lookup_async() + to sam_naming_lookup() to replace it + * Fixed a bug in sam_stream_close() where a '\n' was not being sent after + the SAM command + * Fixed a bug where the stream ID was being improperly incremented in + sam_stream_connect() + * Added generic Linux Makefile for non-Debian distributions + +v1.00 2004-06-02 + * First public release diff --git a/apps/sam/c/examples/build.bat b/apps/sam/c/examples/build.bat new file mode 100644 index 0000000000..c236e99e38 --- /dev/null +++ b/apps/sam/c/examples/build.bat @@ -0,0 +1,8 @@ +@echo off + +del dgram-server.o dgram-server.exe +C:\Dev-Cpp\bin\gcc -I../inc -o dgram-server.o -c dgram-server.c +C:\Dev-Cpp\bin\gcc -I../inc -L../lib -o dgram-server.exe dgram-server.o -lsam -lwsock32 +del dgram-client.o dgram-client.exe +C:\Dev-Cpp\bin\gcc -I../inc -o dgram-client.o -c dgram-client.c +C:\Dev-Cpp\bin\gcc -I../inc -L../lib -o dgram-client.exe dgram-client.o -lsam -lwsock32 \ No newline at end of file diff --git a/apps/sam/c/examples/build.sh b/apps/sam/c/examples/build.sh new file mode 100644 index 0000000000..8ee0b9fb7f --- /dev/null +++ b/apps/sam/c/examples/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +rm dgram-server.o dgram-server +gcc -I../inc -o dgram-server.o -c dgram-server.c +gcc -I../inc -L../lib -o dgram-server dgram-server.o -lsam +rm dgram-client.o dgram-client +gcc -I../inc -o dgram-client.o -c dgram-client.c +gcc -I../inc -L../lib -o dgram-client dgram-client.o -lsam diff --git a/apps/sam/c/examples/dgram-client.c b/apps/sam/c/examples/dgram-client.c new file mode 100644 index 0000000000..c2fe6acc0f --- /dev/null +++ b/apps/sam/c/examples/dgram-client.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the author nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include "sam.h" + +static void dgramback(sam_pubkey_t dest, void *data, size_t size); +static void diedback(void); +static void logback(char *s); +static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result); + +/* + * This is an extremely simple echo client which shows you how LibSAM + * datagrams work. We lookup the name 'dgram-server' then send some data to + * him. If everything works, we should get the same data back. + */ +int main(int argc, char* argv[]) +{ + samerr_t rc; + char errstr[SAM_ERRMSG_LEN]; + + /* Hook up the callback functions */ + sam_dgramback = &dgramback; + sam_diedback = &diedback; + sam_logback = &logback; + sam_namingback = &namingback; + + /* Connect to the SAM server */ + rc = sam_connect("localhost", 7656, "dgram-client", SAM_DGRAM, 0); + if (rc != SAM_OK) { + fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc)); + exit(1); + } + /* + * This is equivalent to doing a DNS lookup on the normal internet. Note + * that the dgram-server must already be running for this to work. + * When the lookup completes, we send them some data (see namingback()). + */ + sam_naming_lookup("dgram-server"); + + /* + * Wait for something to happen, then invoke the appropriate callback + */ + while (true) + sam_read_buffer(); + + return 0; +} + +/* + * When we receive some data, print it out to the screen + */ +static void dgramback(sam_pubkey_t dest, void *data, size_t size) +{ + printf("Datagram received: %s\n", (char *)data); + free(data); +} + +/* + * This is called whenever the SAM connection fails (like if the I2P router is + * shut down) + */ +static void diedback(void) +{ + fprintf(stderr, "Lost SAM connection!\n"); + exit(1); +} + +/* + * The logging callback prints any logging messages from LibSAM + */ +static void logback(char *s) +{ + fprintf(stderr, "LibSAM: %s\n", s); +} + +/* + * When a name is resolved, send data to that destination address + */ +static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result) +{ + char *data = "Hello, invisible world!"; + + printf("I got %s's base 64 destination, so now I will send him some " \ + "data...\n", name); + sam_dgram_send(pubkey, data, strlen(data)); + /* + * ^^^ An extra NUL is appended to the data by LibSAM, so it is not + * necessary to send trailing NULs over the wire for strings. This doesn't + * cause problems with binary data, because the NUL isn't included in `size' + * in sam_dgramback(). + * That is why we use strlen(data) instead of strlen(data) + 1. + */ + puts("Datagram sent"); +} diff --git a/apps/sam/c/examples/dgram-server.c b/apps/sam/c/examples/dgram-server.c new file mode 100644 index 0000000000..8a6ec54afe --- /dev/null +++ b/apps/sam/c/examples/dgram-server.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the author nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include "sam.h" + +static void dgramback(sam_pubkey_t dest, void *data, size_t size); +static void diedback(void); +static void logback(char *s); +static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result); + +/* + * This is an extremely simple echo server which shows you how LibSAM + * datagrams work. We echo back every datagram that is sent to us. + */ +int main(int argc, char* argv[]) +{ + samerr_t rc; + char errstr[SAM_ERRMSG_LEN]; + + /* Hook up the callback functions */ + sam_dgramback = &dgramback; + sam_diedback = &diedback; + sam_logback = &logback; + sam_namingback = &namingback; + + /* Connect to the SAM server */ + rc = sam_connect("127.0.0.1", 7656, "dgram-server", SAM_DGRAM, 0); + if (rc != SAM_OK) { + fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc)); + exit(1); + } + + /* + * At this point we just keep polling the buffer, which causes the + * appropriate callbacks to be called whenever something happens + */ + while (true) + sam_read_buffer(); + + return 0; +} + +/* + * When we receive some data, we just ECHO the exact same data back to them + */ +static void dgramback(sam_pubkey_t dest, void *data, size_t size) +{ + puts("Echoing datagram"); + sam_dgram_send(dest, data, size); + free(data); +} + +/* + * This is called whenever the SAM connection fails (like if the I2P router is + * shut down) + */ +static void diedback(void) +{ + fprintf(stderr, "Lost SAM connection!\n"); + exit(1); +} + +/* + * The logging callback prints any logging messages from LibSAM + */ +static void logback(char *s) +{ + fprintf(stderr, "LibSAM: %s\n", s); +} + +/* + * Not used, but the function has to be in the program anyway + */ +static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result) +{ + assert(false); /* we don't do any naming lookups in this program */ +} diff --git a/apps/sam/c/inc/platform.h b/apps/sam/c/inc/platform.h new file mode 100644 index 0000000000..c8487e702f --- /dev/null +++ b/apps/sam/c/inc/platform.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the author nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PLATFORM_H +#define PLATFORM_H + +/* + * Operating system + */ +#define FREEBSD 0 // FreeBSD (untested) +#define MINGW 1 // Windows native (Mingw) +#define LINUX 2 // Linux +#define CYGWIN 3 // Cygwin + +#if OS == MINGW + #define INET_ADDRSTRLEN 16 + #define NO_GETHOSTBYNAME2 + #define NO_INET_ATON /* implies NO_INET_PTON */ + #define NO_INET_NTOP + #define NO_STRL + #define NO_Z_FORMAT + #define WINSOCK +#endif + +#if OS == LINUX + #define NO_GETHOSTBYNAME2 + #define NO_STRL + #define NO_Z_FORMAT +#endif + +#if OS == CYGWIN + #define FAST32_IS_LONG + #define INET_ADDRSTRLEN 16 + #define NO_GETHOSTBYNAME2 + #define NO_INET_NTOP + #define NO_INET_PTON + #define NO_SNPRINTF + #define NO_STRL + #define NO_VSNPRINTF + #define NO_Z_FORMAT +#endif + +/* + * Standard C99 includes - if your compiler doesn't have these, it's time to + * upgrade + */ +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +/* + * System includes + */ +#ifdef WINSOCK + #include <winsock.h> +#else + #include <arpa/inet.h> + #include <sys/select.h> + #include <sys/types.h> + #include <sys/socket.h> +#endif +#include <assert.h> +#include <errno.h> +#ifndef WINSOCK + #include <netdb.h> +#endif +#if defined NO_SNPRINTF || defined NO_VSNPRINTF + #include "snprintf.h" +#endif +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef NO_STRL + #include "strl.h" +#endif +#ifdef WINSOCK + #include <windows.h> +#else + #include <unistd.h> +#endif + +/* + * Platform-dependent variable types + */ +#ifdef WINSOCK + typedef SOCKET socket_t; + typedef signed long ssize_t; +#else + typedef int socket_t; +#endif + +/* + * Prints out the file name, line number, and function name before log message + */ +#define SAMLOG(format, ...) sam_log("%s:%d:%s: " \ + format, __FILE__, __LINE__, __func__, __VA_ARGS__) + +/* + * This is the same as above, except that it doesn't accept any va args + */ +#define SAMLOGS(str) sam_log("%s:%d:%s: " str, __FILE__, __LINE__, __func__) + +/* + * Set this to true if you want the raw SAM commands to be printed on stdout + * (useful for debugging) + */ +#define SAM_WIRETAP false +#if SAM_WIRETAP + #include <ctype.h> +#endif + +#endif /* PLATFORM_H */ diff --git a/apps/sam/c/inc/sam.h b/apps/sam/c/inc/sam.h new file mode 100644 index 0000000000..88af116945 --- /dev/null +++ b/apps/sam/c/inc/sam.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the author nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SAM_H +#define SAM_H +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Lengths + */ +#define SAM_CMD_LEN 128 /* the maximum length a SAM command can be */ +#define SAM_DGRAM_PAYLOAD_MAX 31774 /* max size of a single datagram packet */ +#define SAM_ERRMSG_LEN 23 /* the longest message returned from sam_strerror */ +#define SAM_LOGMSG_LEN 256 /* the longest log message */ +#define SAM_NAME_LEN 256 /* the longest `name' arg for naming lookup callback*/ +#define SAM_STREAM_PAYLOAD_MAX 32768 /* max size of a single stream packet */ +#define SAM_PKCMD_LEN (SAM_PUBKEY_LEN + SAM_CMD_LEN)/*a public key SAM command*/ +#define SAM_PUBKEY_LEN 517 /* it's actually 516, but +1 for '\0' */ +#define SAM_REPLY_LEN 1024 /* the maximum length a SAM non-data reply can be */ + +/* + * Shorten some standard variable types + */ +typedef signed char schar_t; +typedef unsigned char uchar_t; +typedef unsigned int uint_t; +typedef unsigned long ulong_t; +typedef unsigned short ushort_t; + +typedef enum {SAM_STREAM, SAM_DGRAM, SAM_RAW} sam_conn_t; /* SAM connection */ + +typedef char sam_pubkey_t[SAM_PUBKEY_LEN]; /* base 64 public key */ + +typedef struct { + void *data; + size_t size; +} sam_sendq_t; /* sending queue to encourage large stream packet sizes */ + +typedef int_fast32_t sam_sid_t; /* stream id number */ + +typedef enum { /* see sam_strerror() for detailed descriptions of these */ + /* error codes from SAM itself (SAM_OK is not an actual "error") */ + SAM_OK, SAM_CANT_REACH_PEER, SAM_DUPLICATED_DEST, SAM_I2P_ERROR, + SAM_INVALID_KEY, SAM_KEY_NOT_FOUND, SAM_PEER_NOT_FOUND, SAM_TIMEOUT, + SAM_UNKNOWN, + /* error codes from libsam */ + SAM_BAD_VERSION, SAM_CALLBACKS_UNSET, SAM_SOCKET_ERROR, SAM_TOO_BIG +} samerr_t; + +/* + * Public functions + */ + +/* SAM controls */ +extern bool sam_close(void); +extern samerr_t sam_connect(const char *samhost, uint16_t samport, + const char *destname, sam_conn_t style, uint_t tunneldepth); +extern void sam_naming_lookup(const char *name); +extern bool sam_read_buffer(void); +extern char *sam_strerror(samerr_t code); +/* SAM controls - callbacks */ +extern void (*sam_diedback)(void); +extern void (*sam_logback)(char *str); +extern void (*sam_namingback)(char *name, sam_pubkey_t pubkey, + samerr_t result); + +/* Stream commands */ +extern void sam_stream_close(sam_sid_t stream_id); +extern sam_sid_t sam_stream_connect(const sam_pubkey_t dest); +extern samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, + size_t size); +/* Stream commands - callbacks */ +extern void (*sam_closeback)(sam_sid_t stream_id, samerr_t reason); +extern void (*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest); +extern void (*sam_databack)(sam_sid_t stream_id, void *data, size_t size); +extern void (*sam_statusback)(sam_sid_t stream_id, samerr_t result); + +/* Stream send queue */ +extern samerr_t sam_sendq_add(sam_sendq_t *sendq, const void *data, + size_t dsize); +extern sam_sendq_t *sam_sendq_create(void); +extern void sam_sendq_send(sam_sendq_t *sendq, sam_sid_t stream_id); + +/* Datagram commands */ +extern samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, + size_t size); + +/* Datagram commands - callbacks */ +extern void (*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size); + +#ifdef __cplusplus +} +#endif +#endif /* SAM_H */ diff --git a/apps/sam/c/inc/snprintf.h b/apps/sam/c/inc/snprintf.h new file mode 100644 index 0000000000..9faf1961a0 --- /dev/null +++ b/apps/sam/c/inc/snprintf.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the author nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Note: The snprintf.c file retains its original license (at the top of + * snprintf.c) + */ + +#ifndef SNPRINTF_H +#define SNPRINTF_H +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdarg.h> /* for va_list */ +int snprintf (char *str, size_t count, const char *fmt, ...); +int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); + +#ifdef __cplusplus +} +#endif +#endif /* SNPRINTF_H */ diff --git a/apps/sam/c/inc/strl.h b/apps/sam/c/inc/strl.h new file mode 100644 index 0000000000..fece64212d --- /dev/null +++ b/apps/sam/c/inc/strl.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the author nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Note: The strl.c file retains its original license (at the top of strl.c) + */ + +#ifndef STRL_H +#define STRL_H +#ifdef __cplusplus +extern "C" { +#endif + +extern size_t strlcat(char *dst, const char *src, size_t siz); +extern size_t strlcpy(char *dst, const char *src, size_t siz); + +#ifdef __cplusplus +} +#endif +#endif /* STRL_H */ diff --git a/apps/sam/c/src/sam.c b/apps/sam/c/src/sam.c new file mode 100644 index 0000000000..262e86ab17 --- /dev/null +++ b/apps/sam/c/src/sam.c @@ -0,0 +1,1047 @@ +/* + * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the author nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "platform.h" +#include "sam.h" + +static bool sam_hello(void); +static void sam_log(const char *format, ...); +static void sam_parse(char *s); +static ssize_t sam_read1(char *buf, size_t n); +static ssize_t sam_read2(void *buf, size_t n); +static bool sam_readable(void); +static samerr_t sam_session_create(const char *destname, sam_conn_t style, + uint_t tunneldepth); +static bool sam_socket_connect(const char *host, uint16_t port); +static bool sam_socket_resolve(const char *hostname, char *ipaddr); +static ssize_t sam_write(const void *buf, size_t n); + +/* + * Callback functions + * Note: if you add a new callback be sure to check for non-NULL in sam_connect + */ +/* a peer closed the connection */ +void (*sam_closeback)(sam_sid_t stream_id, samerr_t reason) = NULL; +/* a peer connected to us */ +void (*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest) = NULL; +/* a peer sent some stream data (`data' MUST be freed) */ +void (*sam_databack)(sam_sid_t stream_id, void *data, size_t size) = NULL; +/* a peer sent some datagram data (`data' MUST be freed) */ +void (*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size) = NULL; +/* we lost the connection to the SAM host */ +void (*sam_diedback)(void) = NULL; +/* logging callback */ +void (*sam_logback)(char *str) = NULL; +/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */ +void (*sam_namingback)(char *name, sam_pubkey_t pubkey, + samerr_t result) = NULL; +/* our connection to a peer has completed */ +void (*sam_statusback)(sam_sid_t stream_id, samerr_t result) = NULL; + +static socket_t samd; /* The socket descriptor we're using for + communications with SAM */ +static bool samd_connected = false; /* Whether we're connected with SAM */ + +/* + * Closes the connection to the SAM host + * + * Returns: true on success, false on failure + */ +bool sam_close(void) +{ + if (!samd_connected) + return true; + +#ifdef WINSOCK + if (closesocket(samd) == 0) { + samd_connected = false; + return true; +#else + if (close(samd) == 0) { + samd_connected = false; + return true; +#endif + } else { + SAMLOG("Failed closing the SAM connection (%s)", strerror(errno)); + return false; + } +} + +/* + * Connects to the SAM host + * + * samhost - SAM host + * samport - SAM port + * destname - destination name for this program, or TRANSIENT for a random dest + * tunneldepth - length of the I2P tunnels created by this program (longer is + * more anonymous, but slower) + * + * Returns: true on success, false on failure + */ +samerr_t sam_connect(const char *samhost, uint16_t samport, + const char *destname, sam_conn_t style, uint_t tunneldepth) +{ + samerr_t rc; + + if (style == SAM_STREAM) { + if (sam_closeback == NULL || sam_connectback == NULL || + sam_databack == NULL || sam_diedback == NULL || + sam_logback == NULL || sam_namingback == NULL || + sam_statusback == NULL) { + SAMLOGS("Please set callback functions before connecting"); + return SAM_CALLBACKS_UNSET; + } + } else if (style == SAM_DGRAM) { + if (sam_dgramback == NULL || sam_diedback == NULL || + sam_logback == NULL || sam_namingback == NULL) { + SAMLOGS("Please set callback functions before connecting"); + return SAM_CALLBACKS_UNSET; + } + } else if (style == SAM_RAW) { + assert(false); /* not implemented yet */ + } else { + assert(false); /* unknown style */ + } + +#ifdef WINSOCK + /* + * Is Windows retarded or what? + */ + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD(1, 1); + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + SAMLOGS("WSAStartup() failed"); + return SAM_SOCKET_ERROR; + } + if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1 ) { + SAMLOGS("Bad WinSock version"); + return SAM_SOCKET_ERROR; + } +#endif + + if (!sam_socket_connect(samhost, samport)) { + SAMLOG("Couldn't connect to SAM at %s:%u (%s)", + samhost, samport, strerror(errno)); + SAMLOGS("Is your I2P router running?"); + return SAM_SOCKET_ERROR; + } + + if (!sam_hello()) + return SAM_BAD_VERSION; + + rc = sam_session_create(destname, style, tunneldepth); + if (rc != SAM_OK) + return rc; + + SAMLOG("SAM connection open to %s:%u", samhost, samport); + return SAM_OK; +} + +/* + * Sends data to a destination in a datagram + * + * dest - base 64 destination of who we're sending to + * data - the data we're sending + * size - the size of the data + * + * Returns: true on success, false on failure + */ +samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size) +{ + char cmd[SAM_PKCMD_LEN]; + + if (size < 1 || size > SAM_DGRAM_PAYLOAD_MAX) { +#ifdef NO_Z_FORMAT + SAMLOG("Invalid data send size (%u bytes)", size); +#else + SAMLOG("Invalid data send size (%z bytes)", size); +#endif + return SAM_TOO_BIG; + } +#ifdef NO_Z_FORMAT + snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%u\n", + dest, size); +#else + snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%z\n", + dest, size); +#endif + sam_write(cmd, strlen(cmd)); + sam_write(data, size); + + return SAM_OK; +} + +/* + * Sends the first SAM handshake command and checks the reply + * (call sam_session_create() after this) + * + * Returns: true on success, false on reply failure + */ +static bool sam_hello(void) +{ +#define SAM_HELLO_CMD "HELLO VERSION MIN=1.0 MAX=1.0\n" +#define SAM_HELLO_REPLY "HELLO REPLY RESULT=OK VERSION=1.0" + char reply[SAM_REPLY_LEN]; + + sam_write(SAM_HELLO_CMD, strlen(SAM_HELLO_CMD)); + sam_read1(reply, SAM_REPLY_LEN); + if (strncmp(reply, SAM_HELLO_REPLY, strlen(SAM_HELLO_REPLY)) == 0) + return true; + else { + SAMLOGS("HELLO failed"); + return false; + } +} + +/* + * Converts va arg logs into a standard string and runs the callback + */ +static void sam_log(const char *format, ...) +{ + va_list ap; + char s[SAM_LOGMSG_LEN]; + + va_start(ap, format); + vsnprintf(s, sizeof s, format, ap); + va_end(ap); + /*strlcat(s, "\n", sizeof s);*/ + sam_logback(s); + + return; +} + +/* + * Performs a base 64 public key lookup on the specified `name' + * + * name - name to lookup, or ME to lookup our own name + */ +void sam_naming_lookup(const char *name) +{ + char cmd[SAM_CMD_LEN]; + + snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name); + sam_write(cmd, strlen(cmd)); + + return; +} + +/* + * Parses incoming data and calls the appropriate callback + * + * s - string of data that we read (read past tense) + */ +static void sam_parse(char *s) +{ +#define SAM_DGRAM_RECEIVED_REPLY "DATAGRAM RECEIVED" +#define SAM_NAMING_REPLY "NAMING REPLY" +#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK" +#define SAM_NAMING_REPLY_IK "NAMING REPLY RESULT=INVALID_KEY" +#define SAM_NAMING_REPLY_KNF "NAMING REPLY RESULT=KEY_NOT_FOUND" +#define SAM_STREAM_CLOSED_REPLY "STREAM CLOSED" +#define SAM_STREAM_CONNECTED_REPLY "STREAM CONNECTED" +#define SAM_STREAM_RECEIVED_REPLY "STREAM RECEIVED" +#define SAM_STREAM_STATUS_REPLY "STREAM STATUS" +#define SAM_STREAM_STATUS_REPLY_OK "STREAM STATUS RESULT=OK" +#define SAM_STREAM_STATUS_REPLY_CRP "STREAM STATUS RESULT=CANT_REACH_PEER" +#define SAM_STREAM_STATUS_REPLY_I2E "STREAM STATUS RESULT=I2P_ERROR" +#define SAM_STREAM_STATUS_REPLY_IK "STREAM STATUS RESULT=INVALID_KEY" +#define SAM_STREAM_STATUS_REPLY_TO "STREAM STATUS RESULT=TIMEOUT" + + if (strncmp(s, SAM_DGRAM_RECEIVED_REPLY, + strlen(SAM_DGRAM_RECEIVED_REPLY)) == 0) { + char *p; + sam_pubkey_t dest; + size_t size; + void *data; + + p = strchr(s, '='); /* DESTINATION= */ + assert(p != NULL); + p++; + strlcpy(dest, p, sizeof dest); + p = strchr(p, '='); /* SIZE= */ + assert(p != NULL); + p++; + size = strtol(p, NULL, 10); + assert(size != 0); + data = malloc(size + 1); /* +1 for NUL termination, so when we are + receiving a string it will just work and it + won't be necessary to send NUL. When binary + data is sent, the extra NUL character will + just be ignored by the client program, + because it is not added to the size */ + if (sam_read2(data, size) != -1) { + p = data + size; + *p = '\0'; /* see above NUL note */ + sam_dgramback(dest, data, size); /* `data' must be freed */ + } else + free(data); + + return; + + } else if (strncmp(s, SAM_NAMING_REPLY, strlen(SAM_NAMING_REPLY)) == 0) { + char *p; + char *q; + char name[SAM_NAME_LEN]; + + p = strchr(s, '='); /* can't use strrchar because of option + MESSAGE= */ + assert(p != NULL); /* RESULT= */ + p++; + p = strchr(p, '='); /* NAME= */ + assert(p != NULL); + p++; + + if (strncmp(s, SAM_NAMING_REPLY_OK, strlen(SAM_NAMING_REPLY_OK)) == 0) { + sam_pubkey_t pubkey; + + q = strchr(p, ' '); /* ' 'VAL.. */ + assert(q != NULL); + *q = '\0'; + q++; + q = strchr(q, '='); /* VALUE= */ + assert(q != NULL); + q++; + strlcpy(name, p, sizeof name); + strlcpy(pubkey, q, sizeof pubkey); + sam_namingback(name, pubkey, SAM_OK); + + } else if (strncmp(s, SAM_NAMING_REPLY_IK, + strlen(SAM_NAMING_REPLY_IK)) == 0) { + q = strchr(p, ' '); /* ' 'MES.. (optional) */ + if (q != NULL) + *q = '\0'; + strlcpy(name, p, sizeof name); + sam_namingback(name, NULL, SAM_INVALID_KEY); + + } else if (strncmp(s, SAM_NAMING_REPLY_KNF, + strlen(SAM_NAMING_REPLY_KNF)) == 0) { + q = strchr(p, ' '); /* ' 'MES.. (optional) */ + if (q != NULL) + *q = '\0'; + strlcpy(name, p, sizeof name); + sam_namingback(name, NULL, SAM_KEY_NOT_FOUND); + + } else { + q = strchr(p, ' '); /* ' 'MES.. (optional) */ + if (q != NULL) + *q = '\0'; + strlcpy(name, p, sizeof name); + sam_namingback(name, NULL, SAM_UNKNOWN); + } + + return; + + } else if (strncmp(s, SAM_STREAM_CLOSED_REPLY, + strlen(SAM_STREAM_CLOSED_REPLY)) == 0) { + char *p; + sam_sid_t stream_id; + + p = strchr(s, '='); /* can't use strrchar because of option MESSAGE= */ + assert(p != NULL); /* ID= */ + p++; + stream_id = strtol(p, NULL, 10); + assert(stream_id != 0); + p = strchr(p, '='); /* RESULT= */ + assert(p != NULL); + p++; + if (strncmp(p, "OK", strlen("OK")) == 0) + sam_closeback(stream_id, SAM_OK); + else if (strncmp(p, "CANT_REACH_PEER", strlen("CANT_REACH_PEER")) == 0) + sam_closeback(stream_id, SAM_CANT_REACH_PEER); + else if (strncmp(p, "I2P_ERROR", strlen("I2P_ERROR")) == 0) + sam_closeback(stream_id, SAM_I2P_ERROR); + else if (strncmp(p, "PEER_NOT_FOUND", strlen("PEER_NOT_FOUND")) == 0) + sam_closeback(stream_id, SAM_PEER_NOT_FOUND); + else if (strncmp(p, "TIMEOUT", strlen("TIMEOUT")) == 0) + sam_closeback(stream_id, SAM_TIMEOUT); + else + sam_closeback(stream_id, SAM_UNKNOWN); + + return; + + } else if (strncmp(s, SAM_STREAM_CONNECTED_REPLY, + strlen(SAM_STREAM_CONNECTED_REPLY)) == 0) { + char *p; + sam_sid_t stream_id; + sam_pubkey_t dest; + + p = strrchr(s, '='); /* ID= */ + assert(p != NULL); + *p = '\0'; + p++; + stream_id = strtol(p, NULL, 10); + assert(stream_id != 0); + p = strstr(s, "N="); /* DESTINATION= */ + p += 2; + strlcpy(dest, p, sizeof dest); + sam_connectback(stream_id, dest); + + return; + + } else if (strncmp(s, SAM_STREAM_RECEIVED_REPLY, + strlen(SAM_STREAM_RECEIVED_REPLY)) == 0) { + char *p; + sam_sid_t stream_id; + size_t size; + void *data; + + p = strrchr(s, '='); /* SIZE= */ + assert(p != NULL); + p++; + size = strtol(p, NULL, 10); + assert(size != 0); + p -= 6; + *p = '\0'; + p = strrchr(s, '='); /* ID= */ + assert(p != NULL); + p++; + stream_id = strtol(p, NULL, 10); + assert(stream_id != 0); + data = malloc(size + 1); /* +1 for NUL termination, so when we are + receiving a string it will just work and it + won't be necessary to send NUL. When binary + data is sent, the extra NUL character will + just be ignored by the client program, + because it is not added to the size */ + if (sam_read2(data, size) != -1) { + p = data + size; + *p = '\0'; /* see above NUL note */ + sam_databack(stream_id, data, size); /* `data' must be freed */ + } else + free(data); + + return; + + } else if (strncmp(s, SAM_STREAM_STATUS_REPLY, + strlen(SAM_STREAM_STATUS_REPLY)) == 0) { + char *p; + sam_sid_t stream_id; + + p = strchr(s, '='); /* can't use strrchar because of option MESSAGE= */ + assert(p != NULL); /* RESULT= */ + p++; + p = strchr(p, '='); /* ID= */ + assert(p != NULL); + p++; + stream_id = strtol(p, NULL, 10); + assert(stream_id != 0); + if (strncmp(s, SAM_STREAM_STATUS_REPLY_OK, + strlen(SAM_STREAM_STATUS_REPLY_OK)) == 0) + sam_statusback(stream_id, SAM_OK); + else if (strncmp(s, SAM_STREAM_STATUS_REPLY_CRP, + strlen(SAM_STREAM_STATUS_REPLY_CRP)) == 0) + sam_statusback(stream_id, SAM_CANT_REACH_PEER); + else if (strncmp(s, SAM_STREAM_STATUS_REPLY_I2E, + strlen(SAM_STREAM_STATUS_REPLY_I2E)) == 0) + sam_statusback(stream_id, SAM_I2P_ERROR); + else if (strncmp(s, SAM_STREAM_STATUS_REPLY_IK, + strlen(SAM_STREAM_STATUS_REPLY_IK)) == 0) + sam_statusback(stream_id, SAM_INVALID_KEY); + else if (strncmp(s, SAM_STREAM_STATUS_REPLY_TO, + strlen(SAM_STREAM_STATUS_REPLY_TO)) == 0) + sam_statusback(stream_id, SAM_TIMEOUT); + else + sam_statusback(stream_id, SAM_UNKNOWN); + + return; + + } else + SAMLOG("Unknown SAM command received: %s", s); + + return; +} + +/* + * Reads and callbacks everything in the SAM network buffer until it is clear + * + * Returns: true if we read anything, or false if nothing was there + */ +bool sam_read_buffer(void) +{ + bool read_something = false; + char reply[SAM_REPLY_LEN]; + + if (sam_readable()) { + do { + sam_read1(reply, SAM_REPLY_LEN); + read_something = true; + sam_parse(reply); + } while (sam_readable()); + } + + return read_something; +} + +/* + * SAM command reader + * + * Reads up to `n' bytes of a SAM response into the buffer `buf' OR (preferred) + * up to the first '\n' which is replaced with a NUL. The resulting buffer will + * always be NUL-terminated. + * Warning: A full buffer will cause the last character read to be lost (it is + * replaced with a NUL in the buffer). + * + * buf - buffer + * n - number of bytes available in `buf', the buffer + * + * Returns: number of bytes read, or -1 on error + */ +static ssize_t sam_read1(char *buf, size_t n) +{ + size_t nleft; + ssize_t nread; + char *p; + + *buf = '\0'; /* this forces `buf' to be a string even if there is a + sam_read1 error return */ + if (!samd_connected) { + SAMLOGS("Cannot read from SAM because the SAM connection is closed"); + sam_diedback(); + return -1; + } + assert(n > 0); + p = buf; + nleft = n; + while (nleft > 0) { + nread = recv(samd, p, 1, 0); + if (nread == -1) { + if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */ + continue; + else { + SAMLOG("recv() failed: %s", strerror(errno)); + sam_close(); + sam_diedback(); + return -1; + } + } else if (nread == 0) { /* EOF */ + SAMLOGS("Connection closed by the SAM host"); + sam_close(); + sam_diedback(); + return -1; + } + assert(nread == 1); + nleft--; + if (*p == '\n') { /* end of SAM response */ + *p = '\0'; +#if SAM_WIRETAP + printf("<<<< %s\n", buf); +#endif + return n - nleft; + } + p++; + } + /* buffer full, which is considered an error */ + SAMLOGS("sam_read1() buf full"); + p--; + *p = '\0'; /* yes, this causes the loss of the last character */ + + return n - nleft; /* return >= 0 */ +} + +/* + * SAM payload reader + * + * Reads exactly `n' bytes of a SAM data payload into the buffer `buf' + * + * buf - buffer + * n - number of bytes available in `buf' + * + * Returns: number of bytes read, or -1 on error + */ +static ssize_t sam_read2(void *buf, size_t n) +{ + size_t nleft; + ssize_t nread; + void *p; + + if (!samd_connected) { + SAMLOGS("Cannot read from SAM because the SAM connection is closed"); + sam_diedback(); + return -1; + } + assert(n > 0); + p = buf; + nleft = n; + while (nleft > 0) { + nread = recv(samd, p, nleft, 0); + if (nread == -1) { + if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */ + continue; + else { + SAMLOG("recv() failed: %s", strerror(errno)); + sam_close(); + sam_diedback(); + return -1; + } + } else if (nread == 0) { /* EOF */ + SAMLOGS("Connection closed by the SAM host"); + sam_close(); + sam_diedback(); + return -1; + } + nleft -= nread; + p += nread; + } +#if SAM_WIRETAP + printf("<<<< (read2() %d bytes)\n", n); +#endif + assert(nleft == 0);/* <---\ */ + return n - nleft; /* should be equal to initial n */ +} + +/* + * Checks if there is incoming data waiting to be read from the SAM connection + * + * Returns: true if data is waiting, false otherwise + */ +static bool sam_readable(void) +{ + fd_set rset; /* set of readable descriptors */ + struct timeval tv; + int rc; + + if (!samd_connected) { + SAMLOGS("Cannot read from SAM because the SAM connection is closed"); + sam_diedback(); + return false; + } + /* it seems like there should be a better way to do this (i.e. not select)*/ + FD_ZERO(&rset); + FD_SET(samd, &rset); + tv.tv_sec = 0; + tv.tv_usec = 10; + rc = select(samd + 1, &rset, NULL, NULL, &tv); + if (rc == 0) + return false; + else if (rc > 0) + return true; + else { + SAMLOG("select() failed: %s", strerror(errno)); + return false; + } +} + +/* + * Adds data to the send queue + * + * sendq - the send queue + * data - data to add + * dsize - the size of the data + * + * Returns: true on success, false on error + */ +samerr_t sam_sendq_add(sam_sendq_t *sendq, const void *data, size_t dsize) +{ + assert(dsize >= 0); + if (dsize == 0) { + SAMLOGS("dsize is 0 - adding nothing"); + return SAM_OK; + } else if (sendq->size + dsize > SAM_STREAM_PAYLOAD_MAX) { + SAMLOGS("The queue size would exceed the maximum SAM payload size -" \ + " will not add to the queue"); + return SAM_TOO_BIG; + } + + sendq->data = realloc(sendq->data, sendq->size + dsize); + memcpy(sendq->data + sendq->size, data, dsize); + sendq->size += dsize; + + return SAM_OK; +} + +/* + * Creates a data queue for use with sam_stream_send + * + * Returns: pointer to the newly created send queue + */ +sam_sendq_t *sam_sendq_create(void) +{ + sam_sendq_t *sendq; + + sendq = malloc(sizeof(sam_sendq_t)); + sendq->data = NULL; + sendq->size = 0; + + return sendq; +} + +/* + * Sends the data in the send queue to the specified stream + * + * sendq - the send queue + * stream_id - stream number to send to + */ +void sam_sendq_send(sam_sendq_t *sendq, sam_sid_t stream_id) +{ + sam_stream_send(stream_id, sendq->data, sendq->size); + free(sendq); + + return; +} + +/* + * Sends the second SAM handshake command and checks the reply + * + * destname - destination name for this program, or TRANSIENT to create a + * random temporary destination + * tunneldepth - length of the I2P tunnels created by this program + * + * Returns: SAM error code + */ +static samerr_t sam_session_create(const char *destname, sam_conn_t style, + uint_t tunneldepth) +{ +#define SAM_SESSTATUS_REPLY_OK "SESSION STATUS RESULT=OK" +#define SAM_SESSTATUS_REPLY_DD "SESSION STATUS RESULT=DUPLICATED_DEST" +#define SAM_SESSTATUS_REPLY_I2E "SESSION STATUS RESULT=I2P_ERROR" +#define SAM_SESSTATUS_REPLY_IK "SESSION STATUS RESULT=INVALID_KEY" + char cmd[SAM_CMD_LEN * 2]; + char reply[SAM_REPLY_LEN]; + + if (style == SAM_STREAM) { + snprintf(cmd, sizeof cmd, + "SESSION CREATE STYLE=STREAM DESTINATION=%s " \ + "tunnels.depthInbound=%u " \ + "tunnels.depthOutbound=%u\n", + destname, tunneldepth, tunneldepth); + } else if (style == SAM_DGRAM) { + snprintf(cmd, sizeof cmd, + "SESSION CREATE STYLE=DATAGRAM DESTINATION=%s " \ + "i2cp.messageReliability=BestEffort " \ + "tunnels.depthInbound=%u " \ + "tunnels.depthOutbound=%u\n", + destname, tunneldepth, tunneldepth); + } else { /* SAM_RAW */ + assert(false); /* unimplemented */ + } + + sam_write(cmd, strlen(cmd)); + sam_read1(reply, SAM_REPLY_LEN); + if (strncmp(reply, SAM_SESSTATUS_REPLY_OK, + strlen(SAM_SESSTATUS_REPLY_OK)) == 0) + return SAM_OK; + else if (strncmp(reply, SAM_SESSTATUS_REPLY_DD, + strlen(SAM_SESSTATUS_REPLY_DD)) == 0) + return SAM_DUPLICATED_DEST; + else if (strncmp(reply, SAM_SESSTATUS_REPLY_I2E, + strlen(SAM_SESSTATUS_REPLY_I2E)) == 0) + return SAM_I2P_ERROR; + else if (strncmp(reply, SAM_SESSTATUS_REPLY_IK, + strlen(SAM_SESSTATUS_REPLY_IK)) == 0) + return SAM_INVALID_KEY; + else + return SAM_UNKNOWN; +} + +/* + * Connects to a remote host and returns a connected descriptor + * + * host - host name or ip address + * port - port number + * + * Returns: true on sucess, false on error, with errno set + */ +bool sam_socket_connect(const char *host, uint16_t port) +{ + struct sockaddr_in hostaddr; + int rc; + char ipaddr[INET_ADDRSTRLEN]; + + samd = socket(AF_INET, SOCK_STREAM, 0); +#ifdef WINSOCK + if (samd == INVALID_SOCKET) + return false; +#else + if (samd == -1) + return false; +#endif + memset(&hostaddr, 0, sizeof hostaddr); + hostaddr.sin_family = AF_INET; + hostaddr.sin_port = htons(port); + if (!sam_socket_resolve(host, ipaddr)) + return false; +#ifdef NO_INET_ATON + rc = hostaddr.sin_addr.s_addr = inet_addr(ipaddr); +#elif defined NO_INET_PTON + rc = inet_aton(ipaddr, &hostaddr.sin_addr); +#else + rc = inet_pton(AF_INET, ipaddr, &hostaddr.sin_addr); +#endif + if (rc == 0) { + errno = EINVAL; + return false; + } else if (rc == -1) + return false; + + if (connect(samd, (struct sockaddr *)&hostaddr, sizeof hostaddr) == -1) + return false; + + samd_connected = true; + return true; +} + +/* + * Perform a DNS lookup on a hostname + * + * hostname - hostname to resolve + * ipaddr - filled with the ip address + * + * Returns: true on success, false on failure + */ +static bool sam_socket_resolve(const char *hostname, char *ipaddr) +{ + struct hostent *h; + struct in_addr a; + +retry: +#ifdef NO_GETHOSTBYNAME2 + h = gethostbyname(hostname); +#else + h = gethostbyname2(hostname, AF_INET); +#endif + if (h == NULL) { + if (h_errno == TRY_AGAIN) { +#ifdef WINSOCK + Sleep(1000); +#else + sleep(1); +#endif + goto retry; + } else { + SAMLOG("DNS resolution failed for %s", hostname); + errno = ENOENT; + return false; + } + } + a.s_addr = ((struct in_addr *)h->h_addr)->s_addr; +#ifdef NO_INET_NTOP + char *tmp; + tmp = inet_ntoa(a); + strlcpy(ipaddr, tmp, INET_ADDRSTRLEN); /* inet_ntoa() was very poorly designed */ + return true; +#else + if (inet_ntop(AF_INET, &a, ipaddr, INET_ADDRSTRLEN) != NULL) { + return true; + } else { + SAMLOG("inet_ntop() failed: %s", strerror(errno)); + return false; + } +#endif +} + +/* + * Closes the specified stream number + * + * stream_id - stream number to close + */ +void sam_stream_close(sam_sid_t stream_id) +{ + char cmd[SAM_CMD_LEN]; + +#ifdef FAST32_IS_LONG + snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%ld\n", stream_id); +#else + snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%d\n", stream_id); +#endif + sam_write(cmd, strlen(cmd)); + + return; +} + +/* + * Opens a new stream connection to public key destination `dest' + * + * dest - base 64 destination + * + * Returns: stream id number + */ +sam_sid_t sam_stream_connect(const sam_pubkey_t dest) +{ + char cmd[SAM_PKCMD_LEN]; + static sam_sid_t id = 0; + + id++; /* increment the id for the connection */ +#ifdef FAST32_IS_LONG + snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%ld DESTINATION=%s\n", + id, dest); +#else + snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%d DESTINATION=%s\n", + id, dest); +#endif + sam_write(cmd, strlen(cmd)); + + return id; +} + +/* + * Sends data to a SAM stream + * + * stream_id - the stream number to send to + * data - the data to send + * size - the size of the data + * + * Returns: true on success, false on failure + */ +samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size) +{ + char cmd[SAM_CMD_LEN]; + + if (size < 1 || size > SAM_STREAM_PAYLOAD_MAX) { +#ifdef NO_Z_FORMAT + SAMLOG("Invalid data send size (%u bytes) for stream %d", + size, stream_id); +#else + SAMLOG("Invalid data send size (%z bytes) for stream %d", + size, stream_id); +#endif + return SAM_TOO_BIG; + } +#ifdef NO_Z_FORMAT + #ifdef FAST32_IS_LONG + snprintf(cmd, sizeof cmd, "STREAM SEND ID=%ld SIZE=%u\n", + stream_id, size); + #else + snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%u\n", + stream_id, size); + #endif +#else + snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%z\n", + stream_id, size); +#endif + sam_write(cmd, strlen(cmd)); + sam_write(data, size); + + return SAM_OK; +} + +/* + * Converts a SAM error code into a short error message + * + * code - the error code + * + * Returns: error string + */ +char *sam_strerror(samerr_t code) +{ + switch (code) { + case SAM_OK: /* Operation completed succesfully */ + return "Ok"; + case SAM_CANT_REACH_PEER: /* The peer exists, but cannot be reached */ + return "Can't reach peer"; + case SAM_DUPLICATED_DEST: /* The specified Destination is already in + use by another SAM instance */ + return "Duplicated destination"; + case SAM_I2P_ERROR: /* A generic I2P error (e.g. I2CP + disconnection, etc.) */ + return "I2P error"; + case SAM_INVALID_KEY: /* The specified key is not valid (bad + format, etc.) */ + return "Invalid key"; + case SAM_KEY_NOT_FOUND: /* The naming system can't resolve the + given name */ + return "Key not found"; + case SAM_PEER_NOT_FOUND: /* The peer cannot be found on the network*/ + return "Peer not found"; + case SAM_TIMEOUT: /* Timeout while waiting for an event (e.g. + peer answer) */ + return "Timeout"; + /* + * SAM_UNKNOWN deliberately left out (goes to default) + */ + case SAM_BAD_VERSION: /* sam_hello() had an unexpected reply */ + return "Bad SAM version"; + case SAM_CALLBACKS_UNSET: /* Some callbacks are still set to NULL */ + return "Callbacks unset"; + case SAM_SOCKET_ERROR: /* TCP/IP connection to the SAM host:port + failed */ + return "Socket error"; + case SAM_TOO_BIG: /* A function was passed too much data */ + return "Data size too big"; + default: /* An unexpected error happened */ + return "Unknown error"; + } +} + +/* + * Sends `n' bytes to the SAM host + * + * buf - buffer with the data in it + * n - bytes to send + * + * Returns: `n', or -1 on error + */ +static ssize_t sam_write(const void *buf, size_t n) +{ + size_t nleft; + ssize_t nwritten; + const char *p; + + if (!samd_connected) { + SAMLOGS("Cannot write to SAM because the SAM connection is closed"); + sam_diedback(); + return -1; + } +#if SAM_WIRETAP + const uchar_t *cp = buf; + for (size_t x = 0; x < n; x++) { + if (isprint(cp[x])) + printf("%c,", cp[x]); + else + printf("%03d,", cp[x]); + } + printf("\n"); +#endif + p = buf; + nleft = n; + while (nleft > 0) { + nwritten = send(samd, p, nleft, 0); + if (nwritten <= 0) { + if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */ + continue; + else { + SAMLOG("send() failed: %s", strerror(errno)); + sam_close(); + sam_diedback(); + return -1; + } + } + nleft -= nwritten; + p += nwritten; + } +#if SAM_WIRETAP + printf(">>>> (write() %d bytes)\n", n); +#endif + + return n; +} diff --git a/apps/sam/c/src/snprintf.c b/apps/sam/c/src/snprintf.c new file mode 100644 index 0000000000..1f407a4f95 --- /dev/null +++ b/apps/sam/c/src/snprintf.c @@ -0,0 +1,851 @@ +/* + * Copyright Patrick Powell 1995 + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + * + * More Recently: + * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 + * This was ugly. It is still ugly. I opted out of floating point + * numbers, but the formatter understands just about everything + * from the normal C string format, at least as far as I can tell from + * the Solaris 2.5 printf(3S) man page. + * + * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 + * Ok, added some minimal floating point support, which means this + * probably requires libm on most operating systems. Don't yet + * support the exponent (e,E) and sigfig (g,G). Also, fmtint() + * was pretty badly broken, it just wasn't being exercised in ways + * which showed it, so that's been fixed. Also, formated the code + * to mutt conventions, and removed dead code left over from the + * original. Also, there is now a builtin-test, just compile with: + * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm + * and run snprintf for results. + * + * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i + * The PGP code was using unsigned hexadecimal formats. + * Unfortunately, unsigned formats simply didn't work. + * + * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 + * The original code assumed that both snprintf() and vsnprintf() were + * missing. Some systems only have snprintf() but not vsnprintf(), so + * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. + * + * Andrew Tridgell (tridge@samba.org) Oct 1998 + * fixed handling of %.0f + * added test for HAVE_LONG_DOUBLE + * + * Russ Allbery <rra@stanford.edu> 2000-08-26 + * fixed return value to comply with C99 + * fixed handling of snprintf(NULL, ...) + * + * Hrvoje Niksic <hniksic@arsdigita.com> 2000-11-04 + * include <stdio.h> for NULL. + * added support for long long. + * don't declare argument types to (v)snprintf if stdarg is not used. + * + **************************************************************/ + +#include <string.h> +#include <ctype.h> +#include <sys/types.h> + +#ifndef NULL +# define NULL 0 +#endif + +/* varargs declarations: */ + +#include <stdarg.h> +#define HAVE_STDARGS /* let's hope that works everywhere (mj) */ +#define VA_LOCAL_DECL va_list ap +#define VA_START(f) va_start(ap, f) +#define VA_SHIFT(v,t) ; /* no-op for ANSI */ +#define VA_END va_end(ap) + +#ifdef HAVE_LONG_DOUBLE +#define LDOUBLE long double +#else +#define LDOUBLE double +#endif + +#ifdef HAVE_LONG_LONG +# define LLONG long long +#else +# define LLONG long +#endif + +int snprintf (char *str, size_t count, const char *fmt, ...); +int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); + +static int dopr (char *buffer, size_t maxlen, const char *format, + va_list args); +static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max); +static int fmtint (char *buffer, size_t *currlen, size_t maxlen, + LLONG value, int base, int min, int max, int flags); +static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags); +static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); + +/* + * dopr(): poor man's version of doprintf + */ + +/* format read states */ +#define DP_S_DEFAULT 0 +#define DP_S_FLAGS 1 +#define DP_S_MIN 2 +#define DP_S_DOT 3 +#define DP_S_MAX 4 +#define DP_S_MOD 5 +#define DP_S_MOD_L 6 +#define DP_S_CONV 7 +#define DP_S_DONE 8 + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UP (1 << 5) +#define DP_F_UNSIGNED (1 << 6) + +/* Conversion Flags */ +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#define DP_C_LLONG 3 +#define DP_C_LDOUBLE 4 + +#define char_to_int(p) (p - '0') +#define MAX(p,q) ((p >= q) ? p : q) +#define MIN(p,q) ((p <= q) ? p : q) + +static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) +{ + char ch; + LLONG value; + LDOUBLE fvalue; + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + int total; + size_t currlen; + + state = DP_S_DEFAULT; + currlen = flags = cflags = min = 0; + max = -1; + ch = *format++; + total = 0; + + while (state != DP_S_DONE) + { + if (ch == '\0') + state = DP_S_DONE; + + switch(state) + { + case DP_S_DEFAULT: + if (ch == '%') + state = DP_S_FLAGS; + else + total += dopr_outch (buffer, &currlen, maxlen, ch); + ch = *format++; + break; + case DP_S_FLAGS: + switch (ch) + { + case '-': + flags |= DP_F_MINUS; + ch = *format++; + break; + case '+': + flags |= DP_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= DP_F_SPACE; + ch = *format++; + break; + case '#': + flags |= DP_F_NUM; + ch = *format++; + break; + case '0': + flags |= DP_F_ZERO; + ch = *format++; + break; + default: + state = DP_S_MIN; + break; + } + break; + case DP_S_MIN: + if (isdigit((unsigned char)ch)) + { + min = 10*min + char_to_int (ch); + ch = *format++; + } + else if (ch == '*') + { + min = va_arg (args, int); + ch = *format++; + state = DP_S_DOT; + } + else + state = DP_S_DOT; + break; + case DP_S_DOT: + if (ch == '.') + { + state = DP_S_MAX; + ch = *format++; + } + else + state = DP_S_MOD; + break; + case DP_S_MAX: + if (isdigit((unsigned char)ch)) + { + if (max < 0) + max = 0; + max = 10*max + char_to_int (ch); + ch = *format++; + } + else if (ch == '*') + { + max = va_arg (args, int); + ch = *format++; + state = DP_S_MOD; + } + else + state = DP_S_MOD; + break; + case DP_S_MOD: + switch (ch) + { + case 'h': + cflags = DP_C_SHORT; + ch = *format++; + break; + case 'l': + cflags = DP_C_LONG; + ch = *format++; + break; + case 'L': + cflags = DP_C_LDOUBLE; + ch = *format++; + break; + default: + break; + } + if (cflags != DP_C_LONG) + state = DP_S_CONV; + else + state = DP_S_MOD_L; + break; + case DP_S_MOD_L: + switch (ch) + { + case 'l': + cflags = DP_C_LLONG; + ch = *format++; + break; + default: + break; + } + state = DP_S_CONV; + break; + case DP_S_CONV: + switch (ch) + { + case 'd': + case 'i': + if (cflags == DP_C_SHORT) + value = (short int) va_arg (args, int); + else if (cflags == DP_C_LONG) + value = va_arg (args, long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, LLONG); + else + value = va_arg (args, int); + total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'o': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = (unsigned short int) va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, unsigned LLONG); + else + value = va_arg (args, unsigned int); + total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); + break; + case 'u': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = (unsigned short int) va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, unsigned LLONG); + else + value = va_arg (args, unsigned int); + total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'X': + flags |= DP_F_UP; + case 'x': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = (unsigned short int) va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, unsigned LLONG); + else + value = va_arg (args, unsigned int); + total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); + break; + case 'f': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + /* um, floating point? */ + total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'E': + flags |= DP_F_UP; + case 'e': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + break; + case 'G': + flags |= DP_F_UP; + case 'g': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + break; + case 'c': + total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); + break; + case 's': + strvalue = va_arg (args, char *); + total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); + break; + case 'p': + strvalue = va_arg (args, void *); + total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, + max, flags); + break; + case 'n': + if (cflags == DP_C_SHORT) + { + short int *num; + num = va_arg (args, short int *); + *num = currlen; + } + else if (cflags == DP_C_LONG) + { + long int *num; + num = va_arg (args, long int *); + *num = currlen; + } + else if (cflags == DP_C_LLONG) + { + LLONG *num; + num = va_arg (args, LLONG *); + *num = currlen; + } + else + { + int *num; + num = va_arg (args, int *); + *num = currlen; + } + break; + case '%': + total += dopr_outch (buffer, &currlen, maxlen, ch); + break; + case 'w': + /* not supported yet, treat as next char */ + ch = *format++; + break; + default: + /* Unknown, skip */ + break; + } + ch = *format++; + state = DP_S_DEFAULT; + flags = cflags = min = 0; + max = -1; + break; + case DP_S_DONE: + break; + default: + /* hmm? */ + break; /* some picky compilers need this */ + } + } + if (buffer != NULL) + { + if (currlen < maxlen - 1) + buffer[currlen] = '\0'; + else + buffer[maxlen - 1] = '\0'; + } + return total; +} + +static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max) +{ + int padlen, strln; /* amount to pad */ + int cnt = 0; + int total = 0; + char null[] = "<NULL>"; + + if (value == 0) + { + value = null; + } + + for (strln = 0; value[strln]; ++strln); /* strlen */ + if (max >= 0 && max < strln) + strln = max; + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + while (padlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + } + while (*value && ((max < 0) || (cnt < max))) + { + total += dopr_outch (buffer, currlen, maxlen, *value++); + ++cnt; + } + while (padlen < 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + } + return total; +} + +/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ + +static int fmtint (char *buffer, size_t *currlen, size_t maxlen, + LLONG value, int base, int min, int max, int flags) +{ + int signvalue = 0; + unsigned LLONG uvalue; + char convert[24]; + unsigned int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + const char *digits; + int total = 0; + + if (max < 0) + max = 0; + + uvalue = value; + + if(!(flags & DP_F_UNSIGNED)) + { + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } + else + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else + if (flags & DP_F_SPACE) + signvalue = ' '; + } + + if (flags & DP_F_UP) + /* Should characters be upper case? */ + digits = "0123456789ABCDEF"; + else + digits = "0123456789abcdef"; + + do { + convert[place++] = digits[uvalue % (unsigned)base]; + uvalue = (uvalue / (unsigned)base ); + } while(uvalue && (place < sizeof (convert))); + if (place == sizeof (convert)) place--; + convert[place] = 0; + + zpadlen = max - place; + spadlen = min - MAX ((unsigned int)max, place) - (signvalue ? 1 : 0); + if (zpadlen < 0) zpadlen = 0; + if (spadlen < 0) spadlen = 0; + if (flags & DP_F_ZERO) + { + zpadlen = MAX(zpadlen, spadlen); + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + +#ifdef DEBUG_SNPRINTF + dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", + zpadlen, spadlen, min, max, place)); +#endif + + /* Spaces */ + while (spadlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + --spadlen; + } + + /* Sign */ + if (signvalue) + total += dopr_outch (buffer, currlen, maxlen, signvalue); + + /* Zeros */ + if (zpadlen > 0) + { + while (zpadlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + } + + /* Digits */ + while (place > 0) + total += dopr_outch (buffer, currlen, maxlen, convert[--place]); + + /* Left Justified spaces */ + while (spadlen < 0) { + total += dopr_outch (buffer, currlen, maxlen, ' '); + ++spadlen; + } + + return total; +} + +static LDOUBLE abs_val (LDOUBLE value) +{ + LDOUBLE result = value; + + if (value < 0) + result = -value; + + return result; +} + +static LDOUBLE pow10 (int exp) +{ + LDOUBLE result = 1; + + while (exp) + { + result *= 10; + exp--; + } + + return result; +} + +static long round (LDOUBLE value) +{ + long intpart; + + intpart = value; + value = value - intpart; + if (value >= 0.5) + intpart++; + + return intpart; +} + +static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags) +{ + int signvalue = 0; + LDOUBLE ufvalue; + char iconvert[20]; + char fconvert[20]; + int iplace = 0; + int fplace = 0; + int padlen = 0; /* amount to pad */ + int zpadlen = 0; + int caps = 0; + int total = 0; + LLONG intpart; + LLONG fracpart; + + /* + * AIX manpage says the default is 0, but Solaris says the default + * is 6, and sprintf on AIX defaults to 6 + */ + if (max < 0) + max = 6; + + ufvalue = abs_val (fvalue); + + if (fvalue < 0) + signvalue = '-'; + else + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else + if (flags & DP_F_SPACE) + signvalue = ' '; + +#if 0 + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ +#endif + + intpart = ufvalue; + + /* + * Sorry, we only support 9 digits past the decimal because of our + * conversion method + */ + if (max > 9) + max = 9; + + /* We "cheat" by converting the fractional part to integer by + * multiplying by a factor of 10 + */ + fracpart = round ((pow10 (max)) * (ufvalue - intpart)); + + if (fracpart >= pow10 (max)) + { + intpart++; + fracpart -= pow10 (max); + } + +#ifdef DEBUG_SNPRINTF + dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); +#endif + + /* Convert integer part */ + do { + iconvert[iplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; + intpart = (intpart / 10); + } while(intpart && (iplace < 20)); + if (iplace == 20) iplace--; + iconvert[iplace] = 0; + + /* Convert fractional part */ + do { + fconvert[fplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; + fracpart = (fracpart / 10); + } while(fracpart && (fplace < 20)); + if (fplace == 20) fplace--; + fconvert[fplace] = 0; + + /* -1 for decimal point, another -1 if we are printing a sign */ + padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + zpadlen = max - fplace; + if (zpadlen < 0) + zpadlen = 0; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justifty */ + + if ((flags & DP_F_ZERO) && (padlen > 0)) + { + if (signvalue) + { + total += dopr_outch (buffer, currlen, maxlen, signvalue); + --padlen; + signvalue = 0; + } + while (padlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '0'); + --padlen; + } + } + while (padlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + } + if (signvalue) + total += dopr_outch (buffer, currlen, maxlen, signvalue); + + while (iplace > 0) + total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); + + /* + * Decimal point. This should probably use locale to find the correct + * char to print out. + */ + if (max > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '.'); + + while (fplace > 0) + total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); + } + + while (zpadlen > 0) + { + total += dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + + while (padlen < 0) + { + total += dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + } + + return total; +} + +static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) +{ + if (*currlen + 1 < maxlen) + buffer[(*currlen)++] = c; + return 1; +} + +int vsnprintf (char *str, size_t count, const char *fmt, va_list args) +{ + if (str != NULL) + str[0] = 0; + return dopr(str, count, fmt, args); +} + +/* VARARGS3 */ +#ifdef HAVE_STDARGS +int snprintf (char *str,size_t count,const char *fmt,...) +#else +int snprintf (va_alist) va_dcl +#endif +{ +#ifndef HAVE_STDARGS + char *str; + size_t count; + char *fmt; +#endif + VA_LOCAL_DECL; + int total; + + VA_START (fmt); + VA_SHIFT (str, char *); + VA_SHIFT (count, size_t ); + VA_SHIFT (fmt, char *); + total = vsnprintf(str, count, fmt, ap); + VA_END; + return total; +} + +#ifdef TEST_SNPRINTF +#ifndef LONG_STRING +#define LONG_STRING 1024 +#endif +int main (void) +{ + char buf1[LONG_STRING]; + char buf2[LONG_STRING]; + char *fp_fmt[] = { + "%-1.5f", + "%1.5f", + "%123.9f", + "%10.5f", + "% 10.5f", + "%+22.9f", + "%+4.9f", + "%01.3f", + "%4f", + "%3.1f", + "%3.2f", + "%.0f", + "%.1f", + NULL + }; + double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, + 0.9996, 1.996, 4.136, 0}; + char *int_fmt[] = { + "%-1.5d", + "%1.5d", + "%123.9d", + "%5.5d", + "%10.5d", + "% 10.5d", + "%+22.33d", + "%01.3d", + "%4d", + NULL + }; + long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; + int x, y; + int fail = 0; + int num = 0; + + printf ("Testing snprintf format codes against system sprintf...\n"); + + for (x = 0; fp_fmt[x] != NULL ; x++) + for (y = 0; fp_nums[y] != 0 ; y++) + { + snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); + sprintf (buf2, fp_fmt[x], fp_nums[y]); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", + fp_fmt[x], buf1, buf2); + fail++; + } + num++; + } + + for (x = 0; int_fmt[x] != NULL ; x++) + for (y = 0; int_nums[y] != 0 ; y++) + { + snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); + sprintf (buf2, int_fmt[x], int_nums[y]); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", + int_fmt[x], buf1, buf2); + fail++; + } + num++; + } + printf ("%d tests failed out of %d.\n", fail, num); +} +#endif /* SNPRINTF_TEST */ diff --git a/apps/sam/c/src/strl.c b/apps/sam/c/src/strl.c new file mode 100644 index 0000000000..792ba5ae90 --- /dev/null +++ b/apps/sam/c/src/strl.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stddef.h> +#include <string.h> + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} -- GitLab