Browse Source

Initial commit

Eliezer Croitoru 7 months ago
commit
21666cc94e
6 changed files with 356 additions and 0 deletions
  1. 10 0
      LICENSE
  2. 26 0
      README.md
  3. 15 0
      build.sh
  4. 3 0
      install.sh
  5. 183 0
      sctp-to-tcp/main.go
  6. 119 0
      tcp-to-sctp/main.go

File diff suppressed because it is too large
+ 10 - 0
LICENSE


+ 26 - 0
README.md

@@ -0,0 +1,26 @@
+## SCTP Proxies
+Since Squid-Cache doesn't have any support for SCTP socket, even a non stream based one I wrote these two proxy servers.
+The original goal was to write a fully featured SCTP proxies.
+These ideally should utilize the full capacity of SCTP which is to be able to communicate between two hosts (client and server) while shifting between src and destination IP addresses due to load balancing or failure of one or more routes on the path between the hosts.
+
+Since the Squid-Cache project didn't got enough support these tools are a begining of something.
+
+### TCP to SCTP
+Listens on TCP port like on the loopback(127.0.0.1) of a client with the browser pointed to it and the remote SCTP server ipv4+port are configured as a peer.
+Every new TCP connection is being proxied over SCTP to the remote ipv4+port.
+
+### SCTP to TCP
+Listens on a SCTP ipv4+port (only a single one) and proxies every incomming connection to a local or remote server TCP ipv4+port service.
+The service is able to write a PROXY protocol header V1(only if is bound to one external IPv4 address and not loopback or all interfaces) which Squid-Cache support and there for will be able to enforce static or dynamic(external_acl, ICAP, other) source IP based acl's.
+
+## building the deamons for all OS
+```bash
+./build.sh
+```
+
+## Refrences
+- https://github.com/ishidawataru/sctp
+- https://gist.github.com/legendtkl/c2483c73a3fdb01d36ed8f37d93d3b5c
+- https://github.com/pires/go-proxyproto
+- https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
+- http://gogs.ngtech.co.il/NgTech-LTD/golang-build-software-binaries

+ 15 - 0
build.sh

@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+git clone http://gogs.ngtech.co.il/NgTech-LTD/golang-build-software-binaries
+
+cd tcp-to-sctp
+cp -iv ../golang-build-software-binaries/* ./
+make update
+make all
+cd ..
+
+cd sctp-to-tcp
+cp -iv ../golang-build-software-binaries/* ./
+make update
+make all
+cd ..

+ 3 - 0
install.sh

@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+ehco "not implemented yet"

+ 183 - 0
sctp-to-tcp/main.go

@@ -0,0 +1,183 @@
+package main
+
+import (
+	"errors"
+	"flag"
+	"io"
+	"log"
+	"net"
+	"os"
+	"os/signal"
+	"strconv"
+	"syscall"
+
+	"github.com/asaskevich/govalidator"
+
+	"github.com/ishidawataru/sctp"
+	"github.com/pires/go-proxyproto"
+)
+
+var (
+	dialWithPROXY bool
+	remoteTCPIP   string
+	remoteTCPPort string
+	listenIP      string
+	listenPort    string
+	debug         int
+)
+
+func parseIP(s string) (net.IP, uint16, error) {
+	ip, port, err := net.SplitHostPort(s)
+	if err != nil {
+		return nil, uint16(0), err
+	}
+
+	ip2 := net.ParseIP(ip)
+	if ip2 != nil {
+		return nil, uint16(0), errors.New("invalid IP")
+	}
+
+	i, _ := strconv.Atoi(port)
+	return ip2, uint16(i), nil
+}
+
+func copyConn(src net.Conn) {
+	remoteAddress := src.RemoteAddr()
+	localAddresss := src.LocalAddr()
+
+	dst, err := net.Dial("tcp", remoteTCPIP+":"+remoteTCPPort)
+	if err != nil {
+		if debug > 0 {
+			log.Println("Dial TCP Error:", err.Error(), "To:", remoteTCPIP+":"+remoteTCPPort)
+		}
+		src.Close()
+		return
+	}
+	if debug > 0 {
+		log.Println("Dialed TCP to", remoteTCPIP+":"+remoteTCPPort)
+	}
+
+	if dialWithPROXY {
+		// Write PROXY Protocl Header first
+
+		remoteIP, remotePort, _ := parseIP(remoteAddress.String())
+		localIP, localPort, _ := parseIP(localAddresss.String())
+		if govalidator.IsIPv4(remoteIP.String()) {
+			proxyProtocolV1Header := &proxyproto.Header{Version: byte(1), TransportProtocol: proxyproto.TCPv4, SourceAddress: remoteIP, SourcePort: remotePort, DestinationAddress: localIP, DestinationPort: localPort}
+			wrote, err := proxyProtocolV1Header.WriteTo(src)
+			if err != nil {
+				if debug > 0 {
+					log.Println("Dial TCP Error writing PROXYProtocol header:", err.Error(), "To:", remoteTCPIP+":"+remoteTCPPort)
+				}
+				src.Close()
+				return
+			}
+			if debug > 0 {
+				log.Println("Wrote PROXY Protocol header bytes:", wrote, "To:", remoteTCPIP+":"+remoteTCPPort)
+			}
+		} else {
+			src.Write([]byte("This service supports only Version 4 sources\n"))
+			src.Close()
+			return
+		}
+	}
+
+	done := make(chan struct{})
+
+	go func() {
+		defer src.Close()
+		defer dst.Close()
+		io.Copy(dst, src)
+		done <- struct{}{}
+	}()
+
+	go func() {
+		defer src.Close()
+		defer dst.Close()
+		io.Copy(src, dst)
+		done <- struct{}{}
+	}()
+
+	<-done
+	<-done
+	if debug > 0 {
+		log.Println("Ended connection to:", remoteTCPIP+":"+remoteTCPPort, "From:", remoteAddress, "At:", localAddresss)
+	}
+}
+
+func init() {
+	signalChannel := make(chan os.Signal, 2)
+	signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM)
+	go func() {
+		sig := <-signalChannel
+		switch sig {
+		case os.Interrupt:
+			//handle SIGINT
+			os.Exit(0)
+		case syscall.SIGTERM:
+			//handle SIGTERM
+			os.Exit(0)
+		}
+	}()
+
+	dialWithPROXY = false
+	// remoteTCPIP = "10.0.0.138"
+	// remoteTCPPort = "8080"
+	// listenIP = ""
+	// listenPort = "3128"
+
+	flag.StringVar(&listenIP, "listen-ip", "127.0.0.1", "The IP which the service will listen to")
+	flag.StringVar(&listenPort, "listen-port", "3128", "The Port which the service will listen to")
+
+	flag.StringVar(&remoteTCPIP, "connect-ip", "10.0.0.138", "The IP which the service will proxy to")
+	flag.StringVar(&remoteTCPPort, "connect-port", "8080", "The Port which the service will proxy to")
+
+	// flag.IntVar(&retries, "retries", 4, "The number of http and connect retries")
+	flag.IntVar(&debug, "debug", 0, "The Debug level of the service")
+
+	flag.BoolVar(&dialWithPROXY, "proxy-protocol-connect", false, "Connect to the TCP host with a PROXY protocol header")
+
+	flag.Parse()
+}
+
+func main() {
+
+	log.Println("Starting SCTP-to-TCP service")
+
+	// sctpAddr, _ := sctp.ResolveSCTPAddr("sctp", listenIP+":"+intListenPort)
+
+	// type SCTPAddr struct {
+	// 	IPAddrs []net.IPAddr
+	// 	Port    int
+	// }
+
+	q := net.ParseIP(listenIP)
+	addr := net.IPAddr{IP: q, Zone: ""}
+	var intListenPort int
+	if govalidator.IsPort(listenPort) {
+		intListenPort, _ = strconv.Atoi(listenPort)
+	} else {
+
+	}
+	var addresses []net.IPAddr
+	addresses = append(addresses, addr)
+	sctpAddr := &sctp.SCTPAddr{IPAddrs: addresses, Port: intListenPort}
+
+	ln, err := sctp.ListenSCTP("sctp", sctpAddr)
+	if err != nil {
+		log.Fatalf("failed to listen: %v", err)
+	}
+	log.Printf("Listen on %s", ln.Addr())
+
+	for {
+		conn, err := ln.Accept()
+		if err != nil {
+			log.Println("Failed to SCTP Accept: ", err.Error())
+			continue
+		}
+		if debug > 0 {
+			log.Printf("Accepted Connection from RemoteAddr: %s", conn.RemoteAddr())
+		}
+		go copyConn(conn)
+	}
+}

+ 119 - 0
tcp-to-sctp/main.go

@@ -0,0 +1,119 @@
+package main
+
+import (
+	"flag"
+	"io"
+	"log"
+	"net"
+	"os"
+	"os/signal"
+	"syscall"
+
+	"github.com/ishidawataru/sctp"
+)
+
+var (
+	// dialWithPROXY bool
+	remoteSCTPIP   string
+	remoteSCTPPort string
+	listenIP       string
+	listenPort     string
+	debug          int
+)
+
+func copyConn(src net.Conn) {
+	remoteAddress := src.RemoteAddr()
+	localAddresss := src.LocalAddr()
+
+	addr, _ := sctp.ResolveSCTPAddr("sctp", remoteSCTPIP+":"+remoteSCTPPort)
+	dst, err := sctp.DialSCTP("sctp", nil, addr)
+	if err != nil {
+		if debug > 0 {
+			log.Println("DialSCTP Error:", err.Error(), "To:", remoteSCTPIP+":"+remoteSCTPPort)
+		}
+		src.Close()
+		return
+	}
+	if debug > 0 {
+		log.Printf("Dialed SCTP to %s", dst.RemoteAddr())
+	}
+	done := make(chan struct{})
+
+	go func() {
+		defer src.Close()
+		defer dst.Close()
+		io.Copy(dst, src)
+		done <- struct{}{}
+	}()
+
+	go func() {
+		defer src.Close()
+		defer dst.Close()
+		io.Copy(src, dst)
+		done <- struct{}{}
+	}()
+
+	<-done
+	<-done
+	if debug > 0 {
+		log.Println("Ended connection to:", remoteSCTPIP+":"+remoteSCTPPort, "From:", remoteAddress, "At:", localAddresss)
+	}
+
+}
+
+func init() {
+	signalChannel := make(chan os.Signal, 2)
+	signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM)
+	go func() {
+		sig := <-signalChannel
+		switch sig {
+		case os.Interrupt:
+			//handle SIGINT
+			os.Exit(0)
+		case syscall.SIGTERM:
+			//handle SIGTERM
+			os.Exit(0)
+		}
+	}()
+
+	// dialWithPROXY = false
+	// remoteSCTPIP = "127.0.0.1"
+	// remoteSCTPPort = "3128"
+	// listenIP = ""
+	// listenPort = "3128"
+
+	flag.StringVar(&listenIP, "listen-ip", "127.0.0.1", "The IP which the service will listen to")
+	flag.StringVar(&listenPort, "listen-port", "3128", "The Port which the service will listen to")
+
+	flag.StringVar(&remoteSCTPIP, "connect-ip", "127.0.0.1", "The IP which the service will proxy to")
+	flag.StringVar(&remoteSCTPPort, "connect-port", "3128", "The Port which the service will proxy to")
+
+	// flag.IntVar(&retries, "retries", 4, "The number of http and connect retries")
+	flag.IntVar(&debug, "debug", 0, "The Debug level of the service")
+
+	// flag.BoolVar(&dialWithPROXY, "proxy-protocol-connect", false, "Connect to the SCTP host with a PROXY protocol header")
+
+	flag.Parse()
+}
+
+func main() {
+
+	log.Println("Starting TCP-to-SCTP proxy service")
+
+	ln, err := net.Listen("tcp", listenIP+":"+listenPort)
+	if err != nil {
+		log.Fatal("connection error:" + err.Error())
+	}
+
+	for {
+		conn, err := ln.Accept()
+		if err != nil {
+			log.Printf("failed to accept: %v", err)
+			continue
+		}
+		if debug > 0 {
+			log.Printf("Accepted Connection from RemoteAddr: %s", conn.RemoteAddr())
+		}
+		go copyConn(conn)
+	}
+}