[Pkg-privacy-commits] [onioncat] 01/340: repository restructured

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 13:04:19 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch debian
in repository onioncat.

commit 421fedcdfc89189d06773dcbea37fcc7e8990d6b
Author: eagle <eagle at 58e1ccc2-750e-0410-8d0d-f93ca75ab447>
Date:   Thu Feb 7 00:31:39 2008 +0000

    repository restructured
    
    git-svn-id: http://www.cypherpunk.at/svn/onioncat/trunk@113 58e1ccc2-750e-0410-8d0d-f93ca75ab447
---
 Makefile     |  14 ++
 TODO         |   8 +
 glob_id.txt  |   9 +
 ocat.c       | 189 +++++++++++++++
 ocat.h       | 122 ++++++++++
 ocatlog.c    |  46 ++++
 ocatroute.c  | 762 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ocattun.c    |  88 +++++++
 ocatv6conv.c | 102 ++++++++
 9 files changed, 1340 insertions(+)

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8a75530
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+CC	= gcc
+#PREOPTS	= -DWITHOUT_TUN
+CFLAGS = -pipe -g -Wall $(PREOPTS)
+#LDFLAGS	= -lpthread -lrt
+LDFLAGS	= -lpthread
+TARGET = ocat
+
+all: $(TARGET)
+
+ocat: ocatroute.o ocattun.o ocatv6conv.o ocatlog.o
+
+clean:
+	rm -f *.o $(TARGET)
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..0781415
--- /dev/null
+++ b/TODO
@@ -0,0 +1,8 @@
+//* IPv6 adresses are not checked for their validity
+* frame checking may be improved
+//* garbage collector for packet queue missing
+* garbage collector for session cleanup
+* improve getopt() -- currently many things are hardcoded
+//* packets received on sockets should be validated (Ethertype, ip)
+* licence
+
diff --git a/glob_id.txt b/glob_id.txt
new file mode 100644
index 0000000..9ff8749
--- /dev/null
+++ b/glob_id.txt
@@ -0,0 +1,9 @@
+Global ID generation as in RFC4193
+
+NTP timestamp: cb5020bf.78f29ad6
+EUI-64: 21b:24ff:fe73:cd0e
+=> key: cb5020bf78f29ad6021b24fffe73cd0e
+=> SHA1: bfb7f7153a68682657986bc6659e7b87d87eeb43
+=> global ID: 87d87eeb43
+=> IPv6: FD87:D87E:EB43::/40
+
diff --git a/ocat.c b/ocat.c
new file mode 100644
index 0000000..989be2b
--- /dev/null
+++ b/ocat.c
@@ -0,0 +1,189 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <arpa/inet.h>
+//#include <netinet/in.h>
+//#include <netinet/ip6.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <errno.h>
+#include <time.h>
+#include <pthread.h>
+
+#include "ocat.h"
+
+
+int tunfd_[2] = {0, 1};
+
+extern int debug_level_;
+
+
+/*
+void print_v6_hd(FILE *out, const struct ip6_hdr *ihd)
+{
+   char asip[32], adip[32];
+   char onion[32];
+
+   inet_ntop(AF_INET6, &ihd->ip6_src, asip, 32);
+   inet_ntop(AF_INET6, &ihd->ip6_dst, adip, 32);
+   fprintf(out, "playload: %d\nsrcip: %s\ndstip: %s\n", ntohs(ihd->ip6_ctlun.ip6_un1.ip6_un1_plen), asip, adip);
+   ipv6tonion(&ihd->ip6_dst, onion);
+   fprintf(out, "dst onion: %s\n", onion);
+   fprintf(out, "\n");
+}
+*/
+
+
+void usage(const char *s)
+{
+   fprintf(stderr, "usage: %s [OPTIONS] <onion_hostname>\n"
+         "   -h                    display usage message\n"
+         "   -d <n>                set debug level to n, default = %d\n"
+         "   -i <onion_hostname>   convert onion hostname to IPv6 and exit\n"
+         "   -l <port>             set ocat listen port, default = %d\n"
+         "   -o <ipv6_addr>        convert IPv6 address to onion url and exit\n"
+         "   -r                    run as root, i.e. do not change uid/gid\n"
+         "   -s <port>             set hidden service virtual port, default = %d\n"
+         "   -t <port>             set tor SOCKS port, default = %d\n"
+#ifndef WITHOUT_TUN
+         "   -T <tun_device>       path to tun character device\n"
+#endif
+         "   -v                    validate packets from sockets, default = %d\n"
+         , s, debug_level_, ocat_listen_port_, ocat_dest_port_, tor_socks_port_, vrec_);
+}
+
+
+int main(int argc, char *argv[])
+{
+   char tunname[IFNAMSIZ] = "", onion[ONION_NAME_SIZE], *s, ip6addr[INET6_ADDRSTRLEN];
+   struct in6_addr addr;
+   int c, runasroot = 0;
+   uid_t uid = 504;
+   gid_t gid = 504;
+   int urlconv = 0;
+
+   if (argc < 2)
+      usage(argv[0]), exit(1);
+
+   while ((c = getopt(argc, argv, "d:hriol:t:T:s:")) != -1)
+      switch (c)
+      {
+         case 'd':
+            debug_level_ = atoi(optarg);
+            break;
+
+         case 'i':
+            urlconv = 1;
+            break;
+
+         case 'l':
+            ocat_listen_port_ = atoi(optarg);
+            break;
+
+         case 'o':
+            urlconv = 2;
+            break;
+
+         case 'r':
+            runasroot = 1;
+            break;
+
+         case 's':
+            ocat_dest_port_ = atoi(optarg);
+            break;
+
+         case 't':
+            tor_socks_port_ = atoi(optarg);
+            break;
+
+#ifndef WITHOUT_TUN
+         case 'T':
+            tun_dev_ = optarg;
+            break;
+#endif
+
+         case 'v':
+            vrec_ = 1;
+            break;
+
+         case 'h':
+         default:
+            usage(argv[0]);
+            exit(1);
+      }
+
+   if (!argv[optind])
+      usage(argv[0]), exit(1);
+
+   if (urlconv == 2)
+   {
+      if (inet_pton(AF_INET6, argv[optind], &addr) <= 0)
+         log_msg(L_ERROR, "%s", strerror(errno)), exit(1);
+      if (!has_tor_prefix(&addr))
+         log_msg(L_ERROR, "address does not have TOR prefix"), exit(1);
+      ipv6tonion(&addr, onion);
+      printf("%s.onion\n", onion);
+      exit(0);
+   }
+
+   // convert parameter to IPv6 address
+   strncpy(onion, argv[optind], ONION_NAME_SIZE);
+   if ((s = strchr(onion, '.')))
+         *s = '\0';
+   if (strlen(onion) != 16)
+      log_msg(L_ERROR, "[main] parameter seems not to be valid onion hostname"), exit(1);
+   if (oniontipv6(onion, &addr) == -1)
+      log_msg(L_ERROR, "[main] parameter seems not to be valid onion hostname"), exit(1);
+
+   inet_ntop(AF_INET6, &addr, ip6addr, INET6_ADDRSTRLEN);
+
+   if (urlconv == 1)
+   {
+      printf("%s\n", ip6addr);
+      exit(0);
+   }
+
+   // init peer structure
+   init_peers();
+#ifndef WITHOUT_TUN
+   // create TUN device
+   tunfd_[0] = tunfd_[1] = tun_alloc(tunname, addr);
+#endif
+   log_msg(L_NOTICE, "[main] local IP is %s on %s", ip6addr, tunname);
+   // start socket receiver thread
+   init_socket_receiver();
+   // create listening socket and start socket acceptor
+   init_socket_acceptor();
+   // starting socket cleaner
+   init_socket_cleaner();
+
+/*   // create socks connector thread
+   init_socks_connector();
+   // start packet dequeuer
+   init_packet_dequeuer();
+*/
+   
+   if (!runasroot && !getuid())
+   {
+      log_msg(L_NOTICE, "[main] running as root, changing uid/gid to %d/%d", uid, gid);
+      if (setgid(gid))
+         log_msg(L_ERROR, "[main] could not change gid: \"%s\"", strerror(errno));
+      if (setuid(uid))
+         log_msg(L_ERROR, "[main] could not change uid: \"%d\"", strerror(errno));
+   }
+   log_msg(L_NOTICE, "[main] uid/gid = %d/%d", getuid(), getgid());
+
+   // create socks connector thread
+   init_socks_connector();
+   // start packet dequeuer
+   init_packet_dequeuer();
+
+   // start forwarding packets from tunnel
+   log_msg(L_NOTICE, "[main] starting packet forwarder");
+   packet_forwarder();
+
+   return 0;
+}
+
diff --git a/ocat.h b/ocat.h
new file mode 100644
index 0000000..ee4f9ea
--- /dev/null
+++ b/ocat.h
@@ -0,0 +1,122 @@
+#ifndef OCAT_H
+#define OCAT_H
+
+//#define _POSIX_C_SOURCE 199309L
+
+#include <time.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
+
+//#define BUFLEN 64*1024
+
+#define IP6HLEN sizeof(struct ip6_hdr)
+// TOR prefix : FD87:D87E:EB43::/40
+#define TOR_PREFIX {0xfd,0x87,0xd8,0x7e,0xeb,0x43}
+#define TOR_PREFIX_LEN 48
+#define MAXPEERS 1024
+#define OCAT_LISTEN_PORT 8000
+#define OCAT_DEST_PORT 80
+#define TOR_SOCKS_PORT 9050
+
+
+#define FRAME_SIZE 1504
+#define ONION_NAME_SIZE 23
+
+#define DEQUEUER_WAKEUP 3
+//! maximum number a packet stays in queue
+#define MAX_QUEUE_DELAY 10
+
+#define MAX_IDLE_TIME 120
+#define CLEANER_WAKEUP 10
+
+#define L_INFO 0
+#define L_NOTICE 1
+#define L_ERROR 2
+#define L_FATAL 3
+#define L_DEBUG 4
+
+#define E_SOCKS_SOCK -1
+#define E_SOCKS_CONN -2
+#define E_SOCKS_REQ -3
+#define E_SOCKS_RQFAIL -4
+
+//#define PEER_CONNECT 0
+#define PEER_ACTIVE 1
+#define PEER_INCOMING 0
+#define PEER_OUTGOING 1
+
+typedef struct PacketQueue
+{
+   struct PacketQueue *next;
+   struct in6_addr addr;
+   int psize;
+   time_t time;
+   void *data;
+} PacketQueue_t;
+
+typedef struct SocksHdr
+{
+   char ver;
+   char cmd;
+   uint16_t port;
+   struct in_addr addr;
+} SocksHdr_t;
+
+typedef struct OnionPeer
+{
+   struct in6_addr addr;   //<! remote address of peer
+   int tcpfd;              //<! remote file descriptor
+   time_t time;            //<! timestamp of latest packet
+   int state;              //<! status of peer
+   int dir;
+} OnionPeer_t;
+
+extern uint16_t tor_socks_port_;
+extern uint16_t ocat_listen_port_;
+extern uint16_t ocat_dest_port_;
+extern int vrec_;
+extern int tunfd_[2];
+
+#ifndef WITHOUT_TUN
+#define TUN_DEV "/dev/net/tun"
+extern char *tun_dev_;
+#endif
+
+/* ocatlog.c */
+void log_msg(int, const char *, ...);
+
+/* ocatsv6.c -- this function is sourced out
+ * here because of conflicting headers. */
+void set_ipv6_addr(int, struct in6_addr, int);
+
+/* ocatv6conv.c */
+void ipv6tonion(const struct in6_addr *, char *);
+int oniontipv6(const char *, struct in6_addr *);
+int has_tor_prefix(const struct in6_addr *);
+
+/* ocattun.c */
+#ifndef WITHOUT_TUN
+int tun_alloc(char *, struct in6_addr);
+#endif
+
+/* ocatroute.c */
+OnionPeer_t *search_peer(const struct in6_addr *);
+OnionPeer_t *establish_peer(int fd, const struct in6_addr *);
+void init_peers(void);
+void init_socket_acceptor(void);
+void init_socket_receiver(void);
+void init_socks_connector(void);
+//void push_socks_connector(const struct in6_addr *);
+//int socks_connect(const char *);
+//void *socket_receiver(void *p);
+//void update_peer_time(const OnionPeer_t *);
+//const OnionPeer_t *forward_packet(const struct in6_addr *, const char *, int);
+//void queue_packet(const struct in6_addr *, const char *, int);
+void init_packet_dequeuer(void);
+void packet_forwarder(void);
+void init_socket_cleaner(void);
+
+
+#endif
+
diff --git a/ocatlog.c b/ocatlog.c
new file mode 100644
index 0000000..263bb5d
--- /dev/null
+++ b/ocatlog.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <pthread.h>
+
+#include "ocat.h"
+
+
+int debug_level_ = 4;
+static pthread_mutex_t log_mutex_ = PTHREAD_MUTEX_INITIALIZER;
+static const char *flty_[] = {"info", "notice", "error", "fatal", "debug"};
+
+
+void log_msg(int lf, const char *fmt, ...)
+{
+   unsigned tid;
+   struct tm *tm;
+   time_t t;
+   FILE *out = stderr;
+   char timestr[32] = "";
+   va_list ap;
+
+   if (debug_level_ < lf || lf < 0)
+      return;
+
+   t = time(NULL);
+   tm = localtime(&t);
+   if (tm)
+      strftime(timestr, 32, "%c", tm);
+   tid = (unsigned) pthread_self();
+
+   pthread_mutex_lock(&log_mutex_);
+   fprintf(out, "%s [%08x] %6s ", timestr, tid, flty_[lf]);
+
+   va_start(ap, fmt);
+   vfprintf(out, fmt, ap);
+   va_end(ap);
+
+   fprintf(out, "\n");
+   pthread_mutex_unlock(&log_mutex_);
+}
+
diff --git a/ocatroute.c b/ocatroute.c
new file mode 100644
index 0000000..77c4ddc
--- /dev/null
+++ b/ocatroute.c
@@ -0,0 +1,762 @@
+/*! ocatroute.c
+ *  Contains functions for managing both kind of TCP peers.
+ *  Those are active SOCKS4A and passive TCP-LISTEN.
+ *
+ *  @author Bernhard Fischer <rahra _at_ cypherpunk at>
+ *  @version 2008/02/03-01
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+
+#include "ocat.h"
+
+
+// file descriptor of tcp listener
+static int sockfd_;
+// file descriptors of socket_receiver pipe
+// used for internal communication
+static int lpfd_[2];
+// file descriptors of socks_connector pipe
+// used for internal communication
+static int cpfd_[2];
+// array of active peers
+static OnionPeer_t peer_[MAXPEERS];
+// mutex for locking array of peers
+pthread_mutex_t peer_mutex_ = PTHREAD_MUTEX_INITIALIZER;
+// packet queue pointer
+static PacketQueue_t *queue_ = NULL;
+// mutex and condition variable for packet queue
+static pthread_mutex_t queue_mutex_ = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t queue_cond_ = PTHREAD_COND_INITIALIZER;
+
+uint16_t tor_socks_port_ = TOR_SOCKS_PORT;
+uint16_t ocat_listen_port_ = OCAT_LISTEN_PORT;
+uint16_t ocat_dest_port_ = OCAT_DEST_PORT;
+
+int vrec_ = 0;
+
+
+void init_peers(void)
+{
+   memset(peer_, 0, sizeof(OnionPeer_t) * MAXPEERS);
+}
+
+
+OnionPeer_t *search_peer(const struct in6_addr *addr)
+{
+   int i;
+
+   for (i = 0; i < MAXPEERS; i++)
+      if (!memcmp(addr, &peer_[i].addr, sizeof(struct in6_addr)))
+         break;
+         //return &peer_[i];
+
+   if (i >= MAXPEERS)
+      return NULL;
+
+   return &peer_[i];
+}
+
+
+OnionPeer_t *get_empty_peer(void)
+{
+   int i;
+
+   for (i = 0; i < MAXPEERS; i++)
+      if (!peer_[i].state)
+         return &peer_[i];
+
+   return NULL;
+}
+
+
+void delete_peer(OnionPeer_t *peer)
+{
+   memset(peer, 0, sizeof(OnionPeer_t));
+}
+
+
+const OnionPeer_t *forward_packet(const struct in6_addr *addr, const char *buf, int buflen)
+{
+   OnionPeer_t *peer;
+
+   pthread_mutex_lock(&peer_mutex_);
+   if ((peer = search_peer(addr)))
+   {
+      log_msg(L_DEBUG, "[forwarding_packet]");
+      write(peer->tcpfd, buf, buflen);
+      peer->time = time(NULL);
+   }
+   pthread_mutex_unlock(&peer_mutex_);
+
+   return peer;
+}
+
+
+void queue_packet(const struct in6_addr *addr, const char *buf, int buflen)
+{
+   PacketQueue_t *queue;
+
+   log_msg(L_DEBUG, "[queue_packet] copying packet to heap for queue");
+   if (!(queue = malloc(sizeof(PacketQueue_t) + buflen)))
+   {
+      log_msg(L_ERROR, "[queue_packet] %s for packet to queue", strerror(errno));
+      return;
+   }
+
+   memcpy(&queue->addr, addr, sizeof(struct in6_addr));
+   queue->psize = buflen;
+   queue->data = ((char*)queue) + sizeof(PacketQueue_t);
+   memcpy(queue->data, buf, buflen);
+   queue->time = time(NULL);
+
+   log_msg(L_DEBUG, "[queue_packet] queuing packet");
+   pthread_mutex_lock(&queue_mutex_);
+   queue->next = queue_;
+   queue_ = queue;
+   log_msg(L_DEBUG, "[queue_packet] waking up dequeuer");
+   pthread_cond_signal(&queue_cond_);
+   pthread_mutex_unlock(&queue_mutex_);
+}
+
+
+void *packet_dequeuer(void *p)
+{
+   PacketQueue_t **queue, *fqueue;
+   OnionPeer_t *peer;
+   struct timespec ts;
+   int rc, timed = 0;
+   time_t delay;
+
+   log_msg(L_NOTICE, "[packet_dequeuer] running");
+   for (;;)
+   {
+      pthread_mutex_lock(&queue_mutex_);
+      if (timed)
+      {
+         clock_gettime(CLOCK_REALTIME, &ts);
+         ts.tv_sec += DEQUEUER_WAKEUP;
+         log_msg(L_DEBUG, "[packet_dequeuer] timed conditional wait...");
+         rc = pthread_cond_timedwait(&queue_cond_, &queue_mutex_, &ts);
+      }
+      else
+      {
+         log_msg(L_DEBUG, "[packet_dequeuer] conditional wait...");
+         rc = pthread_cond_wait(&queue_cond_, &queue_mutex_);
+      }
+
+      if (rc)
+         log_msg(L_FATAL, "[packet_dequeuer] woke up: \"%s\"", strerror(rc));
+
+      log_msg(L_DEBUG, "[packet_dequeuer] starting dequeuing");
+      for (queue = &queue_; *queue; /*queue = &(*queue)->next*/)
+      {
+         //FIXME: this could be more performant of locking is done outside of for(...)
+         pthread_mutex_lock(&peer_mutex_);
+         if ((peer = search_peer(&(*queue)->addr)))
+         {
+            write(peer->tcpfd, (*queue)->data, (*queue)->psize);
+            peer->time = time(NULL);
+         }
+         pthread_mutex_unlock(&peer_mutex_);
+
+         // delete packet from queue if it was sent or is too old
+         delay = time(NULL) - (*queue)->time;
+         if (peer || (delay > MAX_QUEUE_DELAY))
+         {
+            fqueue = *queue;
+            *queue = (*queue)->next;
+            free(fqueue);
+            log_msg(L_DEBUG, "[packet_dequeuer] packet dequeued, delay = %d", delay);
+            continue;
+         }
+         queue = &(*queue)->next;
+      }
+      timed = queue_ != NULL;
+      pthread_mutex_unlock(&queue_mutex_);
+   }
+}
+
+
+void init_packet_dequeuer(void)
+{
+   pthread_t thread;
+   int rc;
+
+   if ((rc = pthread_create(&thread, NULL, packet_dequeuer, NULL)))
+      log_msg(L_FATAL, "[init_packet_dequeuer] could not start socket_receiver thread: \"%s\"", strerror(rc));
+}
+
+
+const static char hdigit_[] = "0123456789abcdef";
+
+void hex_code_header(const char *frame, int len, char *buf)
+{
+   int i;
+
+   for (i = 0; i < len; i++, frame++)
+   {
+      *buf++ = hdigit_[(*frame >> 4) & 0x0f];
+      *buf++ = hdigit_[*frame & 0x0f];
+      *buf++ = ' ';
+   }
+   *--buf = '\0';
+}
+
+
+// do some packet validation
+int validate_frame(const char *frame, int len)
+{
+   char buf[INET6_ADDRSTRLEN];
+   struct ip6_hdr *ihd = (struct ip6_hdr*) (frame + 4);
+   char hexbuf[(IP6HLEN + 4) * 3 + 1];
+
+   hex_code_header(frame, len > IP6HLEN + 4 ? IP6HLEN + 4 : len, hexbuf);
+   log_msg(L_DEBUG, "[validate_frame] header \"%s\"", hexbuf);
+
+   if (len < IP6HLEN + 4)
+   {
+      log_msg(L_ERROR, "[validate_frame] frame too short: %d bytes", len);
+      return 0;
+   }
+   if (/*(buf[2] != (char)0x86) || (buf[3] != (char)0xdd)*/ *((uint16_t*) &frame[2]) != htons(0x86dd))
+   {
+      log_msg(L_ERROR, "[validate_frame] ethertype is not IPv6");
+      return 0;
+   }
+   if (!has_tor_prefix(&ihd->ip6_dst))
+   {
+      log_msg(L_ERROR, "[validate_frame] destination %s unreachable", inet_ntop(AF_INET6, &ihd->ip6_dst, buf, INET6_ADDRSTRLEN));
+      return 0;
+   }
+   if (!has_tor_prefix(&ihd->ip6_src))
+   {
+      log_msg(L_ERROR, "[validate_frame] source address invalid. Remote ocat could not reply");
+      return 0;
+   }
+   return ntohs(ihd->ip6_plen);
+}
+
+
+void cleanup_socket(int fd, OnionPeer_t *peer)
+{
+   log_msg(L_NOTICE, "[cleanup_socket] fd %d reached EOF, closing.", fd);
+   close(fd);
+   pthread_mutex_lock(&peer_mutex_);
+   delete_peer(peer);
+   pthread_mutex_unlock(&peer_mutex_);
+}
+
+
+void *socket_receiver(void *p)
+{
+   int i, fd, maxfd, len, state, plen, rlen;
+   char buf[FRAME_SIZE];
+   char addr[INET6_ADDRSTRLEN];
+   fd_set rset;
+//   struct ip6_hdr *ihd;
+
+   log_msg(L_DEBUG, "[socket_receiver] running");
+   for (;;)
+   {
+      FD_ZERO(&rset);
+      FD_SET(lpfd_[0], &rset);
+      maxfd = lpfd_[0];
+
+      // create set for all available peers to read
+      pthread_mutex_lock(&peer_mutex_);
+      for (i = 0; i < MAXPEERS; i++)
+      {
+         // only select active peers
+         if (peer_[i].state != PEER_ACTIVE)
+            continue;
+         if ((fd = peer_[i].tcpfd) >= FD_SETSIZE)
+            log_msg(L_FATAL, "%d >= FD_SETIZE(%d)", fd, FD_SETSIZE), exit(1);
+         FD_SET(fd, &rset);
+         if (fd > maxfd)
+            maxfd = fd;
+      }
+      pthread_mutex_unlock(&peer_mutex_);
+
+      log_msg(L_DEBUG, "[socket_receiver] is selecting...");
+      if (select(maxfd + 1, &rset, NULL, NULL, NULL) == -1)
+      {
+         log_msg(L_FATAL, "[socket_receiver] select encountered error: \"%s\", restarting", strerror(errno));
+         continue;
+      }
+
+      // thread woke up because of internal pipe read => restart selection
+      if (FD_ISSET(lpfd_[0], &rset))
+      {
+         read(lpfd_[0], buf, FRAME_SIZE);
+         continue;
+      }
+
+      //FIXME: should only run until num select returned
+      for (i = 0; i < MAXPEERS; i++)
+      {
+         pthread_mutex_lock(&peer_mutex_);
+         state = peer_[i].state;
+         fd = peer_[i].tcpfd;
+         pthread_mutex_unlock(&peer_mutex_);
+
+         if (state != PEER_ACTIVE)
+            continue;
+
+         if (FD_ISSET(fd, &rset))
+         {
+            log_msg(L_DEBUG, "[socket_receiver] reading from %d", fd);
+
+/*          // *** framed receiver
+            // FIXME: needs packet defragmentation
+            if ((len = read(fd, buf, IP6HLEN + 4)) == -1)
+            {
+               log_msg(L_DEBUG, "[socket_receiver] spurious wakup of %d: \"%s\"", fd, strerror(errno));
+               continue;
+            }
+            // handle EOF
+            if (!len)
+            {
+               cleanup_socket(fd, &peer_[i]);
+               continue;
+            }
+            // validate header
+            if (!(plen = validate_frame(buf, len)))
+            {
+               log_msg(L_ERROR, "[socket_receiver] dropping frame");
+               continue;
+            }
+            // read payload
+            if ((rlen = read(fd, &buf[IP6HLEN + 4], plen)) == -1)
+            {
+               log_msg(L_ERROR, "[socket_receiver] error reading packet payload, dropping frame");
+               continue;
+            }
+
+            // forward payload
+            log_msg(L_DEBUG, "[socket_receiver] sending to tun %d framesize %d", tunfd_, len + rlen);
+            write(tunfd_, buf, len + rlen);
+
+            // cleanup on short read => maybe EOF
+            if (rlen < plen)
+            {
+               log_msg(L_DEBUG, "[socket_receiver] short read on %d, %d < %d", fd, rlen, plen);
+               cleanup_socket(fd, &peer_[i]);
+               continue;
+            }
+*/
+
+/*          // *** unframed receiver
+            // this works, but has a problem if more then one frame is readable at the time
+            // and vrec_ is set (packet validation)
+            if ((len = read(fd, buf, FRAME_SIZE)) > 0)
+            {
+               plen = validate_frame(buf, len);
+               if (vrec_ && !plen)
+               {
+                  log_msg(L_ERROR, "[socket_receiver] dropping frame");
+                  continue;
+               }
+               log_msg(L_DEBUG, "[socket_receiver] sending to tun %d framesize %d", tunfd_, len);
+               write(tunfd_, buf, len);
+            }
+
+            // if len == 0 EOF reached => close session
+            if (!len)
+            {
+               log_msg(L_NOTICE, "[socket_receiver] fd %d reached EOF, closing.", fd);
+               close(fd);
+               pthread_mutex_lock(&peer_mutex_);
+               delete_peer(&peer_[i]);
+               pthread_mutex_unlock(&peer_mutex_);
+               continue;
+            }
+            // this might happen on linux, see SELECT(2)
+            else if (len == -1)
+            {
+               log_msg(L_DEBUG, "[socket_receiver] spurious wakup of %d: \"%s\"", fd, strerror(errno));
+               continue;
+            }
+
+            pthread_mutex_lock(&peer_mutex_);
+            // update timestamp
+            peer_[i].time = time(NULL);
+            // set IP address if it has non yet
+            if (plen && !memcmp(&peer_[i].addr, &in6addr_any, sizeof(struct in6_addr)))
+            {
+               memcpy(&peer_[i].addr, &((struct ip6_hdr*) (buf + 4))->ip6_src, sizeof(struct in6_addr));
+               log_msg(L_NOTICE, "[socket_receiver] incoming connection on %d from %s now identified", fd,
+                     inet_ntop(AF_INET6, &peer_[i].addr, buf, FRAME_SIZE));
+            }
+            pthread_mutex_unlock(&peer_mutex_);
+*/
+            // *** unframed receiver
+            // write reordered after IP validation
+            // this might happen on linux, see SELECT(2)
+            if ((len = read(fd, buf, FRAME_SIZE)) == -1)
+            {
+               log_msg(L_DEBUG, "[socket_receiver] spurious wakup of %d: \"%s\"", fd, strerror(errno));
+               continue;
+            }
+            // if len == 0 EOF reached => close session
+            if (!len)
+            {
+               log_msg(L_NOTICE, "[socket_receiver] fd %d reached EOF, closing.", fd);
+               close(fd);
+               pthread_mutex_lock(&peer_mutex_);
+               delete_peer(&peer_[i]);
+               pthread_mutex_unlock(&peer_mutex_);
+               continue;
+            }
+            // check frame
+            plen = validate_frame(buf, len);
+            if (vrec_ && !plen)
+            {
+               log_msg(L_ERROR, "[socket_receiver] dropping frame");
+               continue;
+            }
+
+            pthread_mutex_lock(&peer_mutex_);
+            // update timestamp
+            peer_[i].time = time(NULL);
+            // set IP address if it is not set yet and frame is valid
+            if (plen && !memcmp(&peer_[i].addr, &in6addr_any, sizeof(struct in6_addr)))
+            {
+               memcpy(&peer_[i].addr, &((struct ip6_hdr*) (buf + 4))->ip6_src, sizeof(struct in6_addr));
+               log_msg(L_NOTICE, "[socket_receiver] incoming connection on %d from %s is now identified", fd,
+                     inet_ntop(AF_INET6, &peer_[i].addr, addr, INET6_ADDRSTRLEN));
+            }
+            pthread_mutex_unlock(&peer_mutex_);
+            log_msg(L_DEBUG, "[socket_receiver] writing to tun %d framesize %d", tunfd_[1], len);
+            write(tunfd_[1], buf, len);
+         }
+      }
+   }
+}
+
+
+void init_socket_receiver(void)
+{
+   pthread_t thread;
+   int rc;
+
+   if (pipe(lpfd_) < 0)
+      log_msg(L_FATAL, "[init_socket_receiver] could not create pipe for socket_receiver: \"%s\"", strerror(errno)), exit(1);
+
+   if ((rc = pthread_create(&thread, NULL, socket_receiver, NULL)))
+      log_msg(L_FATAL, "[init_socket_receiver] could not start socket_receiver thread: \"%s\"", strerror(rc));
+
+/* thread should never terminate
+   if (pthread_detach(thread))
+      log_msg(L_ERROR, "could not detach socket_receiver thread"); */
+}
+
+
+void set_nonblock(int fd)
+{
+   long flags;
+
+   if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+   {
+      log_msg(L_ERROR, "[set_nonblock] could not get socket flags for %d: \"%s\"", fd, strerror(errno));
+      flags = 0;
+   }
+
+   log_msg(L_DEBUG, "[set_nonblock] O_NONBLOCK currently is %x", flags & O_NONBLOCK);
+
+   if ((fcntl(socket, F_SETFL, flags | O_NONBLOCK)) == -1)
+      log_msg(L_ERROR, "[set_nonblock] could not set O_NONBLOCK for %d: \"%s\"", fd, strerror(errno));
+}
+
+
+void insert_peer(int fd, const struct in6_addr *addr)
+{
+   OnionPeer_t *peer;
+
+   log_msg(L_DEBUG, "[inserting_peer] %d", fd);
+
+   set_nonblock(fd);
+
+   pthread_mutex_lock(&peer_mutex_);
+   peer = get_empty_peer();
+   peer->tcpfd = fd;
+   peer->state = PEER_ACTIVE;
+   peer->time = time(NULL);
+   if (addr)
+   {
+      memcpy(&peer->addr, addr, sizeof(struct in6_addr));
+      peer->dir = PEER_OUTGOING;
+   }
+   else
+      peer->dir = PEER_INCOMING;
+   pthread_mutex_unlock(&peer_mutex_);
+
+   // wake up socket_receiver
+   log_msg(L_DEBUG, "[inser_peer] waking up socket_receiver");
+   write(lpfd_[1], &fd, 1);
+}
+
+
+void *socket_acceptor(void *p)
+{
+//   struct ReceiverInfo *fwinfo;
+//   OnionPeer_t *peer;
+   int fd;
+
+   log_msg(L_NOTICE, "[socket_acceptor] running");
+   for (;;)
+   {
+      log_msg(L_DEBUG, "[socket acceptor] is accepting further connections");
+      if ((fd = accept(sockfd_, NULL, NULL)) < 0)
+         perror("onion_receiver:accept"), exit(1);
+
+      log_msg(L_NOTICE, "[socket acceptor] connection accepted on listener");
+      insert_peer(fd, NULL);
+   }
+
+   return NULL;
+}
+
+
+void init_socket_acceptor(void)
+{
+   struct sockaddr_in in = {AF_INET, htons(ocat_listen_port_), {htonl(INADDR_LOOPBACK)}};
+   pthread_t thread;
+   int rc;
+
+   if ((sockfd_ = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+      log_msg(L_FATAL, "[init_socket_acceptor] could not create listener socker: \"%s\"", strerror(errno)), exit(1);
+
+   if (bind(sockfd_, (struct sockaddr*) &in, sizeof(struct sockaddr_in)) < 0)
+      log_msg(L_FATAL, "[init_socket_acceptor] could not bind listener: \"%s\"", strerror(errno)), exit(1);
+
+   if (listen(sockfd_, 32) < 0)
+      log_msg(L_FATAL, "[init_socket_acceptor] could not bring listener to listening state: \"%s\"", strerror(errno)), exit(1);
+   
+   log_msg(L_NOTICE, "[init_socket_acceptor] created local listener on port %d", ocat_listen_port_);
+
+   if ((rc = pthread_create(&thread, NULL, socket_acceptor, NULL)))
+      log_msg(L_FATAL, "[init_socket_acceptor] could not create socket_acceptor: \"%s\"", strerror(rc)), exit(1);
+}
+
+
+//int socks_connect(const char *onion)
+int socks_connect(const struct in6_addr *addr)
+{
+   struct sockaddr_in in = {AF_INET, htons(tor_socks_port_), {htonl(INADDR_LOOPBACK)}};
+   int fd;
+   char buf[FRAME_SIZE], onion[ONION_NAME_SIZE];
+   SocksHdr_t *shdr = (SocksHdr_t*) buf;
+
+   log_msg(L_DEBUG, "[socks_connect] called");
+
+   ipv6tonion(addr, onion);
+   strcat(onion, ".onion");
+
+   log_msg(L_NOTICE, "[socks_connect] trying to connecto to \"%s\" [%s]", onion, inet_ntop(AF_INET6, addr, buf, FRAME_SIZE));
+
+   if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+      return E_SOCKS_SOCK;
+
+   if (connect(fd, (struct sockaddr*) &in, sizeof(in)) < 0)
+   {
+      log_msg(L_ERROR, "[socks_connect] connect() failed");
+      close(fd);
+      return E_SOCKS_CONN;
+   }
+
+   log_msg(L_DEBUG, "[socks_connect] connect()");
+
+   shdr->ver = 4;
+   shdr->cmd = 1;
+   shdr->port = htons(ocat_dest_port_);
+   shdr->addr.s_addr = 0x01000000;
+   strcpy(buf + sizeof(SocksHdr_t), "tor6");
+   strcpy(buf + sizeof(SocksHdr_t) + 5, onion);
+
+   write(fd, shdr, sizeof(SocksHdr_t) + strlen(onion) + 6);
+   log_msg(L_DEBUG, "[socks_connect] connect request sent");
+
+   if (read(fd, shdr, sizeof(SocksHdr_t)) < sizeof(SocksHdr_t))
+   {
+      log_msg(L_ERROR, "[socks_connect] short read, closing.");
+      close(fd);
+      return E_SOCKS_REQ;
+   }
+   log_msg(L_DEBUG, "[socks_connect] socks response received");
+
+   if (shdr->ver || (shdr->cmd != 90))
+   {
+      log_msg(L_ERROR, "[socks_connect] request failed, reason = %d", shdr->cmd);
+      close(fd);
+      return E_SOCKS_RQFAIL;
+   }
+   log_msg(L_NOTICE, "[socks_connect] connection to %s successfully opened on fd %d", onion, fd);
+
+   insert_peer(fd, addr);
+
+   return fd;
+}
+
+
+void *socks_connector(void *p)
+{
+   OnionPeer_t *peer;
+   struct in6_addr addr;
+   int len;
+
+   log_msg(L_NOTICE, "[socks_connector] running");
+
+   for (;;)
+   {
+      log_msg(L_DEBUG, "[socks_connector] reading from connector pipe %d", cpfd_[0]);
+      if ((len = read(cpfd_[0], &addr, sizeof(addr))) == -1)
+         log_msg(L_FATAL, "[socks_connector] error reading from connector pipe %d: %s", cpfd_[0], strerror(errno)), exit(1);
+      if (len != sizeof(addr))
+      {
+         log_msg(L_ERROR, "[socks_connector] illegal read on connector pipe %d: %d bytes", cpfd_[0], len);
+         continue;
+      }
+
+      pthread_mutex_lock(&peer_mutex_);
+      peer = search_peer(&addr);
+      pthread_mutex_unlock(&peer_mutex_);
+
+      if (peer)
+      {
+         log_msg(L_NOTICE, "[socks_connector] peer already exists, ignoring");
+         continue;
+      }
+
+      socks_connect(&addr);
+   }
+}
+
+
+void init_socks_connector(void)
+{
+   pthread_t thread;
+   int rc;
+
+   if (pipe(cpfd_) < 0)
+      log_msg(L_FATAL, "[init_socks_connector] could not create pipe for socks_connector: \"%s\"", strerror(errno)), exit(1);
+
+   if ((rc = pthread_create(&thread, NULL, socks_connector, NULL)))
+      log_msg(L_FATAL, "[init_socks_connector] could not start socks_connector thread: \"%s\"", strerror(rc));
+}
+
+
+/*
+void push_socks_connector(const struct in6_addr *addr)
+{
+   log_msg(L_DEBUG, "[push_socks_connector] writing to socks connector pipe %d", cpfd_[1]);
+   write(cpfd_[1], addr, sizeof(*addr));
+}
+*/
+
+
+/*
+int receive_packet(int fd, char *buf)
+{
+   int rlen;
+
+   rlen = read(fd, buf, FRAME_SIZE);
+   log_msg(L_DEBUG, "read frame with framesize %d", rlen);
+
+   return rlen;
+}
+*/
+
+
+void packet_forwarder(void)
+{
+   char buf[FRAME_SIZE];
+   char addr[INET6_ADDRSTRLEN];
+   struct ip6_hdr *ihd = (struct ip6_hdr*) &buf[4];
+   int rlen;
+
+   for (;;)
+   {
+      //rlen = receive_packet(tunfd_, data);
+      rlen = read(tunfd_[0], buf, FRAME_SIZE);
+      log_msg(L_DEBUG, "[packet_forwarder] received on tunfd %d, framesize %d", tunfd_[0], rlen);
+
+      if (!validate_frame(buf, rlen))
+      {
+         log_msg(L_ERROR, "[packet_forwarder] dropping frame");
+         continue;
+      }
+      /*
+      // do some packet validation
+      if (*((uint16_t*) &buf[2]) != htons(0x86dd))
+      {
+         log_msg(L_ERROR, "ethertype is not IPv6, dropping packet");
+         continue;
+      }
+      if (!has_tor_prefix(&ihd->ip6_dst))
+      {
+         log_msg(L_ERROR, "destination %s unreachable, dropping packet", inet_ntop(AF_INET6, &ihd->ip6_dst, buf, FRAME_SIZE));
+         continue;
+      }
+      if (!has_tor_prefix(&ihd->ip6_src))
+      {
+         log_msg(L_ERROR, "source address invalid. Remote ocat could not reply, dropping packet");
+         continue;
+      }
+      */
+
+      if (!forward_packet(&ihd->ip6_dst, buf, rlen))
+      {
+         log_msg(L_NOTICE, "[packet_forwarder] establishing new socks peer");
+         //push_socks_connector(&ihd->ip6_dst);
+         log_msg(L_DEBUG, "[packet_forwarder] writing %s to socks connector pipe %d", inet_ntop(AF_INET6, &ihd->ip6_dst, addr, INET6_ADDRSTRLEN), cpfd_[1]);
+         write(cpfd_[1], &ihd->ip6_dst, sizeof(struct in6_addr));
+         log_msg(L_DEBUG, "[packet_forwarder] queuing packet");
+         queue_packet(&ihd->ip6_dst, buf, rlen);
+      }
+   }
+}
+
+
+void *socket_cleaner(void *p)
+{
+   int i;
+   log_msg(L_NOTICE, "[socket_cleaner] running");
+   for (;;)
+   {
+      sleep(CLEANER_WAKEUP);
+      log_msg(L_DEBUG, "[socket_cleaner] wakeup");
+      pthread_mutex_lock(&peer_mutex_);
+      for (i = 0; i < MAXPEERS; i++)
+      {
+         if (peer_[i].state && peer_[i].time + MAX_IDLE_TIME < time(NULL))
+         {
+            log_msg(L_NOTICE, "[socket_cleaner] peer %d timed out, closing.", peer_[i].tcpfd);
+            close(peer_[i].tcpfd);
+            delete_peer(&peer_[i]);
+         }
+      }
+      pthread_mutex_unlock(&peer_mutex_);
+   }
+}
+
+
+void init_socket_cleaner(void)
+{
+   pthread_t thread;
+   int rc;
+
+   if ((rc = pthread_create(&thread, NULL, socket_cleaner, NULL)))
+      log_msg(L_FATAL, "[init_socket_cleaner] could not start thread: \"%s\"", strerror(rc));
+}
+
diff --git a/ocattun.c b/ocattun.c
new file mode 100644
index 0000000..b3f2f6c
--- /dev/null
+++ b/ocattun.c
@@ -0,0 +1,88 @@
+/*! ocattun.c
+ *  These functions create the TUN device.
+ *
+ *  @author Bernhard Fischer <rahra _at_ cypherpunk at>
+ *  @version 2008/02/03-01
+ */
+
+#ifndef WITHOUT_TUN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <netinet/ip6.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+
+#include "ocat.h"
+
+/* FIXME: this is defined in linux/ipv6.h but including
+ * it conflicts with other headers. */
+struct in6_ifreq 
+{
+   struct in6_addr ifr6_addr;
+   uint32_t ifr6_prefixlen;
+   int ifr6_ifindex;
+};
+
+char *tun_dev_ = TUN_DEV;
+
+
+int tun_alloc(char *dev, struct in6_addr addr)
+{
+   struct ifreq ifr;
+   struct in6_ifreq ifr6;
+//   struct sockaddr_in6 addr;
+   int fd, sfd;
+
+   if( (fd = open(tun_dev_, O_RDWR)) < 0 )
+      perror("open tun"), exit(1);
+
+   memset(&ifr, 0, sizeof(ifr));
+   ifr.ifr_flags = IFF_TUN /*| IFF_NO_PI*/;
+   if(*dev)
+      strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+   if(ioctl(fd, TUNSETIFF, (void *) &ifr) < 0)
+      perror("TUNSETIFF"), exit(1);
+   strcpy(dev, ifr.ifr_name);
+
+   if ((sfd = socket(PF_INET6, SOCK_DGRAM, 0)) < 0)
+      perror("socket"), exit(1);
+
+   if (ioctl(sfd, SIOCGIFINDEX, &ifr ) < 0)
+      perror("SIOCGIFINDEX"), exit(1);
+
+   /*
+   memset(&addr, 0, sizeof(addr));
+   addr.sin6_family = AF_INET6;
+   if (inet_pton(AF_INET6, ipv6, &addr.sin6_addr) < 0)
+      perror("inet_pton"), exit(1);
+
+   ifr6.ifr6_addr = addr.sin6_addr;*/
+   ifr6.ifr6_addr = addr;
+   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+   ifr6.ifr6_prefixlen = TOR_PREFIX_LEN;
+   if (ioctl(sfd, SIOCSIFADDR, &ifr6) < 0)
+      perror("SIOCIFADDR"), exit(1);
+
+   if (ioctl(sfd, SIOCGIFFLAGS, &ifr) < 0)
+      perror("SIOCGIFFLAGS"), exit(1);
+
+   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+   if (ioctl(sfd, SIOCSIFFLAGS, &ifr) < 0) 
+      perror("SIOCSIFFLAGS"), exit(1);
+
+   close(sfd);
+   return fd;
+}              
+ 
+#endif
+
diff --git a/ocatv6conv.c b/ocatv6conv.c
new file mode 100644
index 0000000..24ab611
--- /dev/null
+++ b/ocatv6conv.c
@@ -0,0 +1,102 @@
+/*! ocatv6conv.c
+ *  These functions convert IPv6 addresses to onion URLs
+ *  and vice versa.
+ *
+ *  @author Bernhard Fischer <rahra _at_ cypherpunkt at>
+ *  @version 2008/02/03-01
+ */
+
+#include <ctype.h>
+#include <string.h>
+//#include <stdint.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
+#include "ocat.h"
+
+//static const char BASE32[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','j','k','m','n','o','p','q','r','s','t','v','w','x','y','z'};
+static const char BASE32[] = "abcdefghijklmnopqrstuvwxyz234567";
+static const char tor_prefix_[] = TOR_PREFIX;
+
+
+int has_tor_prefix(const struct in6_addr *addr)
+{
+   return memcmp(addr, tor_prefix_, 6) == 0;
+/*
+   int i;
+
+   for (i = 0; i < 6; i++)
+      if (*(((char*) addr) + i) != tor_prefix_[i])
+         return 0;
+   return 1;*/
+}
+
+
+void set_tor_prefix(struct in6_addr *addr)
+{
+   memcpy(addr, tor_prefix_, 6);
+/*
+   int i;
+
+   for (i = 0; i < 6; i++)
+      *(((char*) addr) + i) = tor_prefix_[i];*/
+}
+
+
+void shl5(char *bin)
+{
+   int i;
+
+   for (i = 0; i < 15; i++)
+   {
+      bin[i] <<= 5;
+      bin[i] |= (bin[i + 1] >> 3) & 0x1f;
+   }
+   bin[i] <<= 5;
+}
+
+
+int oniontipv6(const char *onion, struct in6_addr *ip6)
+{
+   int i, j;
+
+   memset(ip6, 0, sizeof(struct in6_addr));
+
+   for (i = 0; i < 16; i++)
+   {
+      shl5((char*) ip6);
+      for (j = 0; j < 32; j++)
+         if (tolower(onion[i]) == BASE32[j])
+            break;
+      if (j == 32)
+         return -1;
+      *(((char*) ip6) + 15) |= j;
+   }
+   set_tor_prefix(ip6);
+   return 0;
+}
+
+
+void ipv6tonion(const struct in6_addr *ip6, char *onion)
+{
+   int i;
+   char bin[16];
+
+   memcpy(bin, (char*) ip6 + 6, 16);
+
+   for (i = 0; i < 16; i++, onion++)
+   {
+      *onion = BASE32[bin[0] >> 3 & 0x1f];
+      shl5(bin);
+      /*
+      for (j = 0; j < 15; j++)
+      {
+         bin[j] <<= 5;
+         bin[j] |= (bin[j + 1] >> 3) & 0x1f;
+      }
+      */
+   }
+   *onion = '\0';
+}
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/onioncat.git



More information about the Pkg-privacy-commits mailing list