-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
122 lines (104 loc) · 2.69 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package main
import (
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"os"
"path"
)
const BlockSize = 512
var conn *net.UDPConn
var tids = make(map[[6]byte]*os.File)
func sendError(code byte, peer *net.UDPAddr) {
conn.WriteToUDP([]byte{0, 5, 0, code, 0}, peer)
}
func addr2Tid(peer *net.UDPAddr) [6]byte {
var tid []byte
tid = append(tid, peer.IP...)
tid = append(tid[4:], byte(peer.Port>>8), byte(peer.Port&0xff))
return [6]byte(tid)
}
func sendBlock(file *os.File, block uint16, peer *net.UDPAddr) {
response := []byte{0, 3, 0, 0}
binary.BigEndian.PutUint16(response[2:], block)
buffer := make([]byte, BlockSize)
file.Seek(int64(block-1)*BlockSize, io.SeekStart)
n, err := file.Read(buffer)
if err != nil {
/* File size is multiple of block size, reply an empty data packet. */
if errors.Is(err, io.EOF) {
conn.WriteToUDP(response, peer)
delete(tids, addr2Tid(peer))
file.Close()
return
}
fmt.Println("[ERROR] Cannot read file")
sendError(2, peer)
return
}
/**
* File size is NOT multiple of block size.
* The client should know there's no more packet to send.
*/
if n != BlockSize {
buffer = buffer[:n]
delete(tids, addr2Tid(peer))
file.Close()
}
conn.WriteToUDP(append(response, buffer...), peer)
}
func main() {
var err error
addr, _ := net.ResolveUDPAddr("udp", "0.0.0.0:69")
conn, err = net.ListenUDP("udp", addr)
if err != nil {
fmt.Println("[FATAL] Cannot listen on 0.0.0.0:69")
os.Exit(1)
}
fmt.Println("Server stared at 0.0.0.0:69")
for {
var n int
var peer *net.UDPAddr
buffer := make([]byte, 512)
n, peer, err = conn.ReadFromUDP(buffer)
if err != nil || n < 4 || (buffer[1] != 1 && buffer[1] != 4) {
fmt.Println("[ERROR] Invalid UDP packet from", peer, buffer)
sendError(4, peer)
continue
}
buffer = buffer[:n]
if buffer[1] == 1 {
/* Packet is an initial read request */
var i int
for i = 2; i < len(buffer) && buffer[i] != 0; i++ {
}
fileName := string(buffer[2:i])
/* Only support file in the same directories where the server is started */
if i == len(buffer) || path.Base(fileName) != fileName {
fmt.Println("[WARNING] Invalid file name from", peer, fileName)
sendError(4, peer)
continue
}
/* Open file and add TID */
var file *os.File
file, err = os.Open(fileName)
if err != nil {
fmt.Println("[ERROR] File not found:", fileName)
sendError(1, peer)
continue
}
tids[addr2Tid(peer)] = file
sendBlock(file, 1, peer)
} else if buffer[1] == 4 {
/* Packet is an acknowledgment */
file, ok := tids[addr2Tid(peer)]
if !ok {
fmt.Println("Unknown TID from", peer)
continue
}
sendBlock(file, binary.BigEndian.Uint16(buffer[2:4])+1, peer)
}
}
}