fix func by passing conn, go fmt (oops), start http detection

This commit is contained in:
AJ ONeal 2018-07-30 23:51:46 -06:00
parent 64425b41d2
commit 9c507ae68e
1 changed files with 471 additions and 417 deletions

View File

@ -5,6 +5,7 @@ package main
import ( import (
"bufio" "bufio"
"bytes"
"crypto/rand" "crypto/rand"
"encoding/base64" "encoding/base64"
"flag" "flag"
@ -35,7 +36,6 @@ type ConfMailer struct {
From string `yaml:"from,omitempty"` From string `yaml:"from,omitempty"`
} }
type tcpUser struct { type tcpUser struct {
bufConn bufferedConn bufConn bufferedConn
userCount chan int userCount chan int
@ -58,7 +58,7 @@ func (b bufferedConn) Peek(n int) ([]byte, error) {
return b.r.Peek(n) return b.r.Peek(n)
} }
func (b bufferedConn) Buffered() (int) { func (b bufferedConn) Buffered() int {
return b.r.Buffered() return b.r.Buffered()
} }
@ -80,19 +80,22 @@ type myMsg struct {
} }
var firstMsgs chan myMsg var firstMsgs chan myMsg
//var myRooms map[string](chan myMsg) //var myRooms map[string](chan myMsg)
var myMsgs chan myMsg var myMsgs chan myMsg
//var myUnsortedConns map[net.Conn]bool //var myUnsortedConns map[net.Conn]bool
var newConns chan net.Conn var newConns chan net.Conn
var newTcpChat chan bufferedConn var newTcpChat chan bufferedConn
var authTcpChat chan tcpUser var authTcpChat chan tcpUser
var delTcpChat chan bufferedConn var delTcpChat chan bufferedConn
var newHttpChat chan bufferedConn var newHttpChat chan bufferedConn
var newHttpClient chan bufferedConn
var delHttpChat chan bufferedConn var delHttpChat chan bufferedConn
func usage() { func usage() {
fmt.Fprintf(os.Stderr, "\nusage: go run chatserver.go\n") fmt.Fprintf(os.Stderr, "\nusage: go run chatserver.go\n")
flag.PrintDefaults(); flag.PrintDefaults()
fmt.Println() fmt.Println()
os.Exit(1) os.Exit(1)
@ -124,7 +127,7 @@ func handleRaw(bufConn bufferedConn) {
// Handle all subsequent packets // Handle all subsequent packets
buffer := make([]byte, 1024) buffer := make([]byte, 1024)
for { for {
//fmt.Fprintf(os.Stdout, "[raw] Waiting for message...\n"); //fmt.Fprintf(os.Stdout, "[raw] Waiting for message...\n")
count, err := bufConn.Read(buffer) count, err := bufConn.Read(buffer)
if nil != err { if nil != err {
if io.EOF != err { if io.EOF != err {
@ -172,13 +175,13 @@ func handleRaw(bufConn bufferedConn) {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Add(1)
go func() { go func() {
time.Sleep(50 * 1000000) time.Sleep(50 * time.Millisecond)
const msg = "Mailing auth code..." const msg = "Mailing auth code..."
for _, r := range msg { for _, r := range msg {
time.Sleep(20 * 1000000) time.Sleep(20 * time.Millisecond)
fmt.Fprintf(bufConn, string(r)) fmt.Fprintf(bufConn, string(r))
} }
time.Sleep(50 * 1000000) time.Sleep(50 * time.Millisecond)
wg.Done() wg.Done()
}() }()
if "" != config.Mailer.ApiKey { if "" != config.Mailer.ApiKey {
@ -200,9 +203,9 @@ func handleRaw(bufConn bufferedConn) {
} }
// so I don't have to actually go check my email // so I don't have to actually go check my email
fmt.Fprintf(os.Stdout, "\n== AUTHORIZATION ==\n[cheat code for %s]: %s\n", email, code) fmt.Fprintf(os.Stdout, "\n== AUTHORIZATION ==\n[cheat code for %s]: %s\n", email, code)
time.Sleep(150 * 1000000) time.Sleep(150 * time.Millisecond)
fmt.Fprintf(bufConn, " done\n") fmt.Fprintf(bufConn, " done\n")
time.Sleep(150 * 1000000) time.Sleep(150 * time.Millisecond)
fmt.Fprintf(bufConn, "Auth Code: ") fmt.Fprintf(bufConn, "Auth Code: ")
continue continue
} }
@ -211,7 +214,7 @@ func handleRaw(bufConn bufferedConn) {
fmt.Fprintf(bufConn, "Incorrect Code\nAuth Code: ") fmt.Fprintf(bufConn, "Incorrect Code\nAuth Code: ")
} else { } else {
authn = true authn = true
time.Sleep(150 * 1000000) time.Sleep(150 * time.Millisecond)
fmt.Fprintf(bufConn, "\n") fmt.Fprintf(bufConn, "\n")
u := tcpUser{ u := tcpUser{
bufConn: bufConn, bufConn: bufConn,
@ -221,18 +224,18 @@ func handleRaw(bufConn bufferedConn) {
authTcpChat <- u authTcpChat <- u
// prevent data race on len(myRawConns) // prevent data race on len(myRawConns)
// XXX (there can't be a race between these two lines, right?) // XXX (there can't be a race between these two lines, right?)
count := <- u.userCount count := <-u.userCount
u.userCount = nil u.userCount = nil
time.Sleep(50 * 1000000) time.Sleep(50 * time.Millisecond)
fmt.Fprintf(bufConn, "\n") fmt.Fprintf(bufConn, "\n")
time.Sleep(50 * 1000000) time.Sleep(50 * time.Millisecond)
fmt.Fprintf(bufConn, "Welcome to #general (%d users)!", count) fmt.Fprintf(bufConn, "Welcome to #general (%d users)!", count)
time.Sleep(50 * 1000000) time.Sleep(50 * time.Millisecond)
fmt.Fprintf(bufConn, "\n") fmt.Fprintf(bufConn, "\n")
time.Sleep(50 * 1000000) time.Sleep(50 * time.Millisecond)
// TODO /help /join <room> /users /channels /block <user> /upgrade <http/ws> // TODO /help /join <room> /users /channels /block <user> /upgrade <http/ws>
//fmt.Fprintf(bufConn, "(TODO `/help' for list of commands)") //fmt.Fprintf(bufConn, "(TODO `/help' for list of commands)")
time.Sleep(100 * 1000000) time.Sleep(100 * time.Millisecond)
fmt.Fprintf(bufConn, "\n") fmt.Fprintf(bufConn, "\n")
// this would be cool, but won't work since other messages will come // this would be cool, but won't work since other messages will come
@ -242,7 +245,7 @@ func handleRaw(bufConn bufferedConn) {
continue continue
} }
//fmt.Fprintf(os.Stdout, "Queing message...\n"); //fmt.Fprintf(os.Stdout, "Queing message...\n")
//myRooms["general"] <- myMsg{ //myRooms["general"] <- myMsg{
myMsgs <- myMsg{ myMsgs <- myMsg{
receivedAt: time.Now(), receivedAt: time.Now(),
@ -258,6 +261,7 @@ func handleRaw(bufConn bufferedConn) {
func handleSorted(conn bufferedConn) { func handleSorted(conn bufferedConn) {
// Wish List for protocol detection // Wish List for protocol detection
// * PROXY protocol (and loop) // * PROXY protocol (and loop)
// * HTTP CONNECT (proxy) (and loop)
// * tls (and loop) https://github.com/polvi/sni // * tls (and loop) https://github.com/polvi/sni
// * http/ws // * http/ws
// * irc // * irc
@ -269,11 +273,50 @@ func handleSorted(conn bufferedConn) {
// Note: Realistically no tls/http/irc client is going to send so few bytes // Note: Realistically no tls/http/irc client is going to send so few bytes
// (and no router is going to chunk so small) // (and no router is going to chunk so small)
// that it cannot reasonably detect the protocol in the first packet // that it cannot reasonably detect the protocol in the first packet
// However, typical MTU is 1,500 and HTTP can have a 2k URL
// so expecting to find the "HTTP/1.1" in the Peek is not always reasonable
n := conn.Buffered() n := conn.Buffered()
firstMsg, err := conn.Peek(n) firstMsg, err := conn.Peek(n)
if nil != err { if nil != err {
panic(err) conn.Close()
return
} }
var protocol string
// between A and z
if firstMsg[0] >= 65 && firstMsg[0] <= 122 {
i := bytes.Index(firstMsg, []byte(" /"))
if -1 != i {
protocol = "HTTP"
// very likely HTTP
j := bytes.IndexAny(firstMsg, "\r\n")
if -1 != j {
k := bytes.Index(bytes.ToLower(firstMsg[:j]), []byte("HTTP/1"))
if -1 != k {
// positively HTTP
}
}
}
} else if 0x16 /*22*/ == firstMsg[0] {
// Because I don't always remember off the top of my head what the first byte is
// http://blog.fourthbit.com/2014/12/23/traffic-analysis-of-an-ssl-slash-tls-session
// https://tlseminar.github.io/first-few-milliseconds/
// TODO I want to learn about ALPN
protocol = "TLS"
}
if "" == protocol {
fmt.Fprintf(conn, "\n\nWelcome to Sample Chat! You're not an HTTP client, assuming Telnet.\nYou must authenticate via email to participate\n\nEmail: ")
newTcpChat <- conn
return
} else if "HTTP" != protocol {
defer conn.Close()
fmt.Fprintf(conn, "\n\nNot yet supported. Try HTTP or Telnet\n\n")
return
}
newHttpClient <- conn
/*
firstMsgs <- myMsg{ firstMsgs <- myMsg{
receivedAt: time.Now(), receivedAt: time.Now(),
sender: conn, sender: conn,
@ -289,7 +332,7 @@ func handleSorted(conn bufferedConn) {
// Handle all subsequent packets // Handle all subsequent packets
buf := make([]byte, 1024) buf := make([]byte, 1024)
for { for {
fmt.Fprintf(os.Stdout, "[sortable] Waiting for message...\n"); fmt.Fprintf(os.Stdout, "[sortable] Waiting for message...\n")
count, err := conn.Read(buf) count, err := conn.Read(buf)
if nil != err { if nil != err {
if io.EOF != err { if io.EOF != err {
@ -312,6 +355,7 @@ func handleSorted(conn bufferedConn) {
channel: "general", channel: "general",
} }
} }
*/
} }
func handleConnection(netConn net.Conn) { func handleConnection(netConn net.Conn) {
@ -337,15 +381,14 @@ func handleConnection(netConn net.Conn) {
} }
//fmt.Fprintf(os.Stdout, "[First Byte] %s\n", fmsg) //fmt.Fprintf(os.Stdout, "[First Byte] %s\n", fmsg)
m.Lock(); m.Lock()
if virgin { if virgin {
virgin = false virgin = false
newHttpChat <- bufConn newHttpChat <- bufConn
} else { } else {
// TODO probably needs to go into a channel
newTcpChat <- bufConn newTcpChat <- bufConn
} }
m.Unlock(); m.Unlock()
}() }()
time.Sleep(250 * 1000000) time.Sleep(250 * 1000000)
@ -378,7 +421,7 @@ func sendAuthCode(cnf ConfMailer, to string) (string, error) {
form := url.Values{} form := url.Values{}
form.Add("from", cnf.From) form.Add("from", cnf.From)
form.Add("to", to) form.Add("to", to)
form.Add("subject", "Sample Chat Auth Code: " + code) form.Add("subject", "Sample Chat Auth Code: "+code)
form.Add("text", text) form.Add("text", text)
form.Add("html", html) form.Add("html", html)
@ -414,6 +457,7 @@ func sendAuthCode(cnf ConfMailer, to string) (string, error) {
} }
var config Conf var config Conf
func main() { func main() {
flag.Usage = usage flag.Usage = usage
port := flag.Uint("telnet-port", 0, "tcp telnet chat port") port := flag.Uint("telnet-port", 0, "tcp telnet chat port")
@ -438,6 +482,7 @@ func main() {
authTcpChat = make(chan tcpUser, 128) authTcpChat = make(chan tcpUser, 128)
newTcpChat = make(chan bufferedConn, 128) newTcpChat = make(chan bufferedConn, 128)
newHttpChat = make(chan bufferedConn, 128) newHttpChat = make(chan bufferedConn, 128)
newHttpClient = make(chan bufferedConn, 128)
//myUnsortedConns = make(map[net.Conn]bool) //myUnsortedConns = make(map[net.Conn]bool)
// TODO dynamically select on channels? // TODO dynamically select on channels?
@ -458,7 +503,7 @@ func main() {
fmt.Fprintf(os.Stderr, "Couldn't bind to TCP socket %q: %s\n", addr, err) fmt.Fprintf(os.Stderr, "Couldn't bind to TCP socket %q: %s\n", addr, err)
os.Exit(2) os.Exit(2)
} }
fmt.Println("Listening on", addr); fmt.Println("Listening on", addr)
go func() { go func() {
for { for {
@ -471,14 +516,20 @@ func main() {
newConns <- conn newConns <- conn
} }
}() }()
/*
server := &http.Server{
Addr: addr,
Handler: &myHandler{},
}
*/
// Main event loop handling most access to shared data // Main event loop handling most access to shared data
for { for {
select { select {
case conn := <- newConns: case conn := <-newConns:
// This is short lived // This is short lived
go handleConnection(conn) go handleConnection(conn)
case u := <- authTcpChat: case u := <-authTcpChat:
// allow to receive messages // allow to receive messages
// (and be counted among the users) // (and be counted among the users)
myRawConns[u.bufConn] = true myRawConns[u.bufConn] = true
@ -492,17 +543,19 @@ func main() {
channel: "general", channel: "general",
email: "system", email: "system",
} }
case bufConn := <- newTcpChat: case bufConn := <-newTcpChat:
go handleRaw(bufConn) go handleRaw(bufConn)
case bufConn := <- delTcpChat: case bufConn := <-delTcpChat:
// we can safely ignore this error // we can safely ignore this error
bufConn.Close() bufConn.Close()
delete(myRawConns, bufConn) delete(myRawConns, bufConn)
case bufConn := <- newHttpChat: case bufConn := <-newHttpChat:
go handleSorted(bufConn) go handleSorted(bufConn)
//case msg := <- myRooms["general"]: //case msg := <- myRooms["general"]:
//delete(myRooms["general"], bufConn) //delete(myRooms["general"], bufConn)
case msg := <- myMsgs: //case bufConn := <-newHttpClient:
//server.Serve(bufConn)
case msg := <-myMsgs:
t := msg.receivedAt t := msg.receivedAt
tf := "%d-%02d-%02d %02d:%02d:%02d (%s)" tf := "%d-%02d-%02d %02d:%02d:%02d (%s)"
var sender string var sender string
@ -516,7 +569,7 @@ func main() {
zone, _ := msg.receivedAt.Zone() zone, _ := msg.receivedAt.Zone()
//ts, err := msg.receivedAt.MarshalJSON() //ts, err := msg.receivedAt.MarshalJSON()
fmt.Fprintf(os.Stdout, tf + " [%s] (%s):\n\t%s", fmt.Fprintf(os.Stdout, tf+" [%s] (%s):\n\t%s",
t.Year(), t.Month(), t.Day(), t.Year(), t.Month(), t.Day(),
t.Hour(), t.Minute(), t.Second(), zone, t.Hour(), t.Minute(), t.Second(), zone,
sender, sender,
@ -528,23 +581,24 @@ func main() {
continue continue
} }
// To ask: Why do I have to pass in conn to prevent a data race? Is it garbage collection?
// Don't block the rest of the loop // Don't block the rest of the loop
// TODO maybe use a chan to send to the socket's event loop // TODO maybe use a chan to send to the socket's event loop
go func() { go func(conn bufferedConn) {
// Protect against malicious clients to prevent DoS // Protect against malicious clients to prevent DoS
// https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/ // https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
timeoutDuration := 5 * time.Second timeoutDuration := 5 * time.Second
conn.SetWriteDeadline(time.Now().Add(timeoutDuration)) conn.SetWriteDeadline(time.Now().Add(timeoutDuration))
_, err := fmt.Fprintf(conn, tf + " [%s]: %s", _, err := fmt.Fprintf(conn, tf+" [%s]: %s",
t.Year(), t.Month(), t.Day(), t.Year(), t.Month(), t.Day(),
t.Hour(), t.Minute(), t.Second(), zone, t.Hour(), t.Minute(), t.Second(), zone,
msg.email, msg.bytes) msg.email, msg.bytes)
if nil != err { if nil != err {
delTcpChat <- conn delTcpChat <- conn
} }
}() }(conn)
} }
case msg := <- firstMsgs: case msg := <-firstMsgs:
fmt.Fprintf(os.Stdout, "f [First Message]\n") fmt.Fprintf(os.Stdout, "f [First Message]\n")
ts, err := msg.receivedAt.MarshalJSON() ts, err := msg.receivedAt.MarshalJSON()
if nil != err { if nil != err {
@ -552,7 +606,7 @@ func main() {
} }
fmt.Fprintf(os.Stdout, "f [Timestamp] %s\n", ts) fmt.Fprintf(os.Stdout, "f [Timestamp] %s\n", ts)
fmt.Fprintf(os.Stdout, "f [Remote] %s\n", msg.sender.RemoteAddr().String()) fmt.Fprintf(os.Stdout, "f [Remote] %s\n", msg.sender.RemoteAddr().String())
fmt.Fprintf(os.Stdout, "f [Message] %s\n", msg.bytes); fmt.Fprintf(os.Stdout, "f [Message] %s\n", msg.bytes)
} }
} }
} }