Skip to content

Instantly share code, notes, and snippets.

@Ell
Created April 16, 2016 19:10
Show Gist options
  • Save Ell/b666a989928c6b6292eb35fd2dc7f564 to your computer and use it in GitHub Desktop.
Save Ell/b666a989928c6b6292eb35fd2dc7f564 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"encoding/binary"
"errors"
log "github.com/Sirupsen/logrus"
"net"
)
type server struct {
conn *net.UDPConn
}
type packet struct {
address *net.UDPAddr
buf []byte
}
type recvConnect struct {
ConnectionID int64
Action int32
TransactionID int32
}
type replyConnect struct {
Action int32
TransactionID int32
ConnectionID int64
}
type recvAnnounce struct {
ConnectionID int64
Action int32
TransactionID int32
InfoHash [20]byte
PeerID [20]byte
Downloaded int64
Left int64
Uploaded int64
Event int32
IP uint32
Key uint32
NumWant int32
Port uint16
Extensions uint16
}
type replyAnnounce struct {
Action int32
TransactionID int32
Interval int32
Leechers int32
Seeders int32
}
type peer struct {
IP int32
Port uint16
}
type recvScrape struct {
ConnectionID int64
Action int32
TransactionID int32
InfoHashes [][20]byte
}
type replyScrape struct {
Action int32
TransactionID int32
InfoHashes []infoHash
}
type infoHash struct {
Complete int32
Downloaded int32
Incomplete int32
}
type trackerError struct {
Action int32
TransactionID int32
ErrorString []byte
}
// StartServer starts a new UDP tracker instance
func StartServer(config *Config) error {
log.SetFormatter(&log.JSONFormatter{})
serverAddress, err := net.ResolveUDPAddr("udp4", config.Server.ListenAddress)
if err != nil {
return err
}
conn, err := net.ListenUDP("udp", serverAddress)
if err != nil {
return err
}
log.Infof("Starting server and listening on %s", serverAddress.String())
s := server{
conn: conn,
}
for {
var buf []byte
size, addr, err := conn.ReadFromUDP(buf)
if err != nil {
log.Error(err)
continue
}
if size <= 0 {
continue
}
p := packet{
address: addr,
buf: buf,
}
if err != nil {
go s.handleErrorResponse(p, err)
} else {
go s.packetHandler(p)
}
}
}
func (s *server) packetHandler(p packet) {
header := struct {
ConnectionID int64
Action int32
TransactionID int32
}{}
b := bytes.NewBuffer(p.buf)
err := binary.Read(b, binary.BigEndian, &header)
if err != nil {
s.handleErrorResponse(p, err)
return
}
switch header.Action {
case 0:
go s.handleConnectRequest(p)
case 1:
go s.handleAnnounceRequest(p)
case 2:
go s.handleScrapeRequest(p)
default:
go s.handleErrorResponse(p, errors.New("Unhandled action type: "+string(header.Action)))
}
}
func (s *server) handleConnectRequest(p packet) {
logRequestInfo(p, "Got connect request")
}
func (s *server) handleAnnounceRequest(p packet) {
logRequestInfo(p, "Got announce request")
}
func (s *server) handleScrapeRequest(p packet) {
logRequestInfo(p, "Got scrape request")
}
func (s *server) handleErrorResponse(p packet, err error) {
log.WithFields(log.Fields{
"address": p.address.String(),
"error": err.Error(),
"body": p.buf,
}).Error(err.Error())
}
func logRequestInfo(p packet, desc string) {
log.WithFields(log.Fields{
"address": p.address.String(),
"body": p.buf,
}).Info(desc)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment