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 (
"bufio"
"bytes"
"crypto/rand"
"encoding/base64"
"flag"
@ -35,7 +36,6 @@ type ConfMailer struct {
From string `yaml:"from,omitempty"`
}
type tcpUser struct {
bufConn bufferedConn
userCount chan int
@ -58,7 +58,7 @@ func (b bufferedConn) Peek(n int) ([]byte, error) {
return b.r.Peek(n)
}
func (b bufferedConn) Buffered() (int) {
func (b bufferedConn) Buffered() int {
return b.r.Buffered()
}
@ -80,19 +80,22 @@ type myMsg struct {
}
var firstMsgs chan myMsg
//var myRooms map[string](chan myMsg)
var myMsgs chan myMsg
//var myUnsortedConns map[net.Conn]bool
var newConns chan net.Conn
var newTcpChat chan bufferedConn
var authTcpChat chan tcpUser
var delTcpChat chan bufferedConn
var newHttpChat chan bufferedConn
var newHttpClient chan bufferedConn
var delHttpChat chan bufferedConn
func usage() {
fmt.Fprintf(os.Stderr, "\nusage: go run chatserver.go\n")
flag.PrintDefaults();
flag.PrintDefaults()
fmt.Println()
os.Exit(1)
@ -124,7 +127,7 @@ func handleRaw(bufConn bufferedConn) {
// Handle all subsequent packets
buffer := make([]byte, 1024)
for {
//fmt.Fprintf(os.Stdout, "[raw] Waiting for message...\n");
//fmt.Fprintf(os.Stdout, "[raw] Waiting for message...\n")
count, err := bufConn.Read(buffer)
if nil != err {
if io.EOF != err {
@ -172,13 +175,13 @@ func handleRaw(bufConn bufferedConn) {
var wg sync.WaitGroup
wg.Add(1)
go func() {
time.Sleep(50 * 1000000)
time.Sleep(50 * time.Millisecond)
const msg = "Mailing auth code..."
for _, r := range msg {
time.Sleep(20 * 1000000)
time.Sleep(20 * time.Millisecond)
fmt.Fprintf(bufConn, string(r))
}
time.Sleep(50 * 1000000)
time.Sleep(50 * time.Millisecond)
wg.Done()
}()
if "" != config.Mailer.ApiKey {
@ -200,9 +203,9 @@ func handleRaw(bufConn bufferedConn) {
}
// 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)
time.Sleep(150 * 1000000)
time.Sleep(150 * time.Millisecond)
fmt.Fprintf(bufConn, " done\n")
time.Sleep(150 * 1000000)
time.Sleep(150 * time.Millisecond)
fmt.Fprintf(bufConn, "Auth Code: ")
continue
}
@ -211,7 +214,7 @@ func handleRaw(bufConn bufferedConn) {
fmt.Fprintf(bufConn, "Incorrect Code\nAuth Code: ")
} else {
authn = true
time.Sleep(150 * 1000000)
time.Sleep(150 * time.Millisecond)
fmt.Fprintf(bufConn, "\n")
u := tcpUser{
bufConn: bufConn,
@ -221,18 +224,18 @@ func handleRaw(bufConn bufferedConn) {
authTcpChat <- u
// prevent data race on len(myRawConns)
// XXX (there can't be a race between these two lines, right?)
count := <- u.userCount
count := <-u.userCount
u.userCount = nil
time.Sleep(50 * 1000000)
time.Sleep(50 * time.Millisecond)
fmt.Fprintf(bufConn, "\n")
time.Sleep(50 * 1000000)
time.Sleep(50 * time.Millisecond)
fmt.Fprintf(bufConn, "Welcome to #general (%d users)!", count)
time.Sleep(50 * 1000000)
time.Sleep(50 * time.Millisecond)
fmt.Fprintf(bufConn, "\n")
time.Sleep(50 * 1000000)
time.Sleep(50 * time.Millisecond)
// TODO /help /join <room> /users /channels /block <user> /upgrade <http/ws>
//fmt.Fprintf(bufConn, "(TODO `/help' for list of commands)")
time.Sleep(100 * 1000000)
time.Sleep(100 * time.Millisecond)
fmt.Fprintf(bufConn, "\n")
// this would be cool, but won't work since other messages will come
@ -242,7 +245,7 @@ func handleRaw(bufConn bufferedConn) {
continue
}
//fmt.Fprintf(os.Stdout, "Queing message...\n");
//fmt.Fprintf(os.Stdout, "Queing message...\n")
//myRooms["general"] <- myMsg{
myMsgs <- myMsg{
receivedAt: time.Now(),
@ -258,6 +261,7 @@ func handleRaw(bufConn bufferedConn) {
func handleSorted(conn bufferedConn) {
// Wish List for protocol detection
// * PROXY protocol (and loop)
// * HTTP CONNECT (proxy) (and loop)
// * tls (and loop) https://github.com/polvi/sni
// * http/ws
// * irc
@ -269,11 +273,50 @@ func handleSorted(conn bufferedConn) {
// Note: Realistically no tls/http/irc client is going to send so few bytes
// (and no router is going to chunk so small)
// 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()
firstMsg, err := conn.Peek(n)
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{
receivedAt: time.Now(),
sender: conn,
@ -289,7 +332,7 @@ func handleSorted(conn bufferedConn) {
// Handle all subsequent packets
buf := make([]byte, 1024)
for {
fmt.Fprintf(os.Stdout, "[sortable] Waiting for message...\n");
fmt.Fprintf(os.Stdout, "[sortable] Waiting for message...\n")
count, err := conn.Read(buf)
if nil != err {
if io.EOF != err {
@ -312,6 +355,7 @@ func handleSorted(conn bufferedConn) {
channel: "general",
}
}
*/
}
func handleConnection(netConn net.Conn) {
@ -337,15 +381,14 @@ func handleConnection(netConn net.Conn) {
}
//fmt.Fprintf(os.Stdout, "[First Byte] %s\n", fmsg)
m.Lock();
m.Lock()
if virgin {
virgin = false
newHttpChat <- bufConn
} else {
// TODO probably needs to go into a channel
newTcpChat <- bufConn
}
m.Unlock();
m.Unlock()
}()
time.Sleep(250 * 1000000)
@ -378,7 +421,7 @@ func sendAuthCode(cnf ConfMailer, to string) (string, error) {
form := url.Values{}
form.Add("from", cnf.From)
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("html", html)
@ -414,6 +457,7 @@ func sendAuthCode(cnf ConfMailer, to string) (string, error) {
}
var config Conf
func main() {
flag.Usage = usage
port := flag.Uint("telnet-port", 0, "tcp telnet chat port")
@ -438,6 +482,7 @@ func main() {
authTcpChat = make(chan tcpUser, 128)
newTcpChat = make(chan bufferedConn, 128)
newHttpChat = make(chan bufferedConn, 128)
newHttpClient = make(chan bufferedConn, 128)
//myUnsortedConns = make(map[net.Conn]bool)
// 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)
os.Exit(2)
}
fmt.Println("Listening on", addr);
fmt.Println("Listening on", addr)
go func() {
for {
@ -471,14 +516,20 @@ func main() {
newConns <- conn
}
}()
/*
server := &http.Server{
Addr: addr,
Handler: &myHandler{},
}
*/
// Main event loop handling most access to shared data
for {
select {
case conn := <- newConns:
case conn := <-newConns:
// This is short lived
go handleConnection(conn)
case u := <- authTcpChat:
case u := <-authTcpChat:
// allow to receive messages
// (and be counted among the users)
myRawConns[u.bufConn] = true
@ -492,17 +543,19 @@ func main() {
channel: "general",
email: "system",
}
case bufConn := <- newTcpChat:
case bufConn := <-newTcpChat:
go handleRaw(bufConn)
case bufConn := <- delTcpChat:
case bufConn := <-delTcpChat:
// we can safely ignore this error
bufConn.Close()
delete(myRawConns, bufConn)
case bufConn := <- newHttpChat:
case bufConn := <-newHttpChat:
go handleSorted(bufConn)
//case msg := <- myRooms["general"]:
//delete(myRooms["general"], bufConn)
case msg := <- myMsgs:
//case bufConn := <-newHttpClient:
//server.Serve(bufConn)
case msg := <-myMsgs:
t := msg.receivedAt
tf := "%d-%02d-%02d %02d:%02d:%02d (%s)"
var sender string
@ -516,7 +569,7 @@ func main() {
zone, _ := msg.receivedAt.Zone()
//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.Hour(), t.Minute(), t.Second(), zone,
sender,
@ -528,23 +581,24 @@ func main() {
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
// 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
// https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
timeoutDuration := 5 * time.Second
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.Hour(), t.Minute(), t.Second(), zone,
msg.email, msg.bytes)
if nil != err {
delTcpChat <- conn
}
}()
}(conn)
}
case msg := <- firstMsgs:
case msg := <-firstMsgs:
fmt.Fprintf(os.Stdout, "f [First Message]\n")
ts, err := msg.receivedAt.MarshalJSON()
if nil != err {
@ -552,7 +606,7 @@ func main() {
}
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 [Message] %s\n", msg.bytes);
fmt.Fprintf(os.Stdout, "f [Message] %s\n", msg.bytes)
}
}
}