Upload server
This commit is contained in:
+409
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* HTTP tunnel over WebRTC - Server implementation
|
||||
* Author: Konstantinos Drakontidis
|
||||
* Email: gedra100sh@gmail.com
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pion/webrtc/v4"
|
||||
)
|
||||
|
||||
var config_t Config
|
||||
|
||||
/*
|
||||
* This function gets:
|
||||
* dc: pointer to data channel
|
||||
* raw: slice of bytes
|
||||
* Receives data from dc, decodes it, creates and sends a HTTP request,
|
||||
* encodes the HTTP response and sends it back to the dc.
|
||||
*/
|
||||
func channelHandler(dc *webrtc.DataChannel, raw []byte) {
|
||||
// Creating a buffer and store the received
|
||||
buf := bytes.NewBuffer(raw)
|
||||
// Creating a decoder and decode the data
|
||||
dec := gob.NewDecoder(buf)
|
||||
var request_t Request
|
||||
decode_error := dec.Decode(&request_t)
|
||||
if decode_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.GOB.DECODE.ERROR, decode_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.GOB.DECODE.SUCCESS)
|
||||
}
|
||||
|
||||
// Constructing the HTTP request
|
||||
req_body := bytes.NewBuffer(request_t.Body)
|
||||
req, req_error := http.NewRequest(request_t.Method, config_t.Config.PROXY_SERVER+request_t.Path, req_body)
|
||||
if req_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.HTTP.REQUEST.ERROR, req_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.HTTP.REQUEST.SUCCESS)
|
||||
}
|
||||
req.Header = request_t.Headers
|
||||
|
||||
// Creating a HTTP client - handles redirect requests as normal requests
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
// Submitting the request
|
||||
res, send_req_error := client.Do(req)
|
||||
if send_req_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.HTTP.SEND_REQUEST.ERROR, send_req_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.HTTP.SEND_REQUEST.SUCCESS)
|
||||
}
|
||||
|
||||
// Constructing the response
|
||||
res_body, read_body_error := io.ReadAll(res.Body)
|
||||
if read_body_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.HTTP.READ.ERROR, read_body_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.HTTP.READ.SUCCESS)
|
||||
}
|
||||
response_t := Response{
|
||||
ID: request_t.ID,
|
||||
Status: res.StatusCode,
|
||||
Headers: res.Header,
|
||||
Body: res_body,
|
||||
}
|
||||
|
||||
// Encoding and sending the response to the data channel
|
||||
var enc_buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&enc_buf)
|
||||
encode_error := enc.Encode(response_t)
|
||||
if encode_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.GOB.ENCODE.ERROR, encode_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.GOB.ENCODE.SUCCESS)
|
||||
}
|
||||
|
||||
data_channel_send_error := dc.Send(enc_buf.Bytes())
|
||||
if data_channel_send_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WEBRTC.SEND_DATA_CHANNEL.ERROR, data_channel_send_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WEBRTC.SEND_DATA_CHANNEL.SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function gets:
|
||||
* conn: pointer to the websocket connection
|
||||
* peer_connections: pointer to map of tracked peer connections
|
||||
* raw: slice of bytes
|
||||
* Receives a peer connection offer, creates a new peer connection, sets the
|
||||
* listeners for it. Sets the remote descriptions, generates, sets and sends the answer.
|
||||
*/
|
||||
func offer(conn *websocket.Conn, peer_connections *map[uuid.UUID]*webrtc.PeerConnection, raw []byte) {
|
||||
// Constructing the offer
|
||||
var offer_t Offer
|
||||
message_unmarshal_error := json.Unmarshal(raw, &offer_t)
|
||||
if message_unmarshal_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.JSON.UNMARSHAL.ERROR, message_unmarshal_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.JSON.UNMARSHAL.SUCCESS)
|
||||
}
|
||||
|
||||
// Creating the peer configuration
|
||||
config := webrtc.Configuration{
|
||||
ICEServers: config_t.Config.ICE_SERVERS,
|
||||
}
|
||||
|
||||
// Creating peer connection
|
||||
var new_peer_error error
|
||||
p2p, new_peer_error := webrtc.NewPeerConnection(config)
|
||||
if new_peer_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WEBRTC.NEW.ERROR, new_peer_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WEBRTC.NEW.SUCCESS)
|
||||
}
|
||||
|
||||
// Setting the peer listeners
|
||||
p2p.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
|
||||
fmt.Printf("ICE: %s\n", is.String())
|
||||
})
|
||||
p2p.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) {
|
||||
fmt.Printf("WebRTC: %s\n", pcs.String())
|
||||
})
|
||||
p2p.OnICECandidate(func(candidate *webrtc.ICECandidate) {
|
||||
// Checking if ICE gathering has finished
|
||||
if candidate != nil {
|
||||
fmt.Print(config_t.Messages.WEBRTC.ON_CANDIDATE)
|
||||
// Constructing candidate, encoding to JSON and sending to websocket
|
||||
candidate_t := Candidate{
|
||||
Type: "candidate",
|
||||
Id: offer_t.Id,
|
||||
Candidate: (*candidate).ToJSON(),
|
||||
}
|
||||
// Create JSON string for candidate and send it to the websocket
|
||||
candidate_msg, message_marshal_error := json.Marshal(candidate_t)
|
||||
if message_marshal_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.JSON.MARSHAL.ERROR, message_marshal_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.JSON.MARSHAL.SUCCESS)
|
||||
}
|
||||
ws_write_error := conn.WriteMessage(websocket.TextMessage, candidate_msg)
|
||||
if ws_write_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WS.WRITE.ERROR, ws_write_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WS.WRITE.SUCCESS)
|
||||
}
|
||||
}
|
||||
})
|
||||
p2p.OnDataChannel(func(dc *webrtc.DataChannel) {
|
||||
dc.OnOpen(func() {
|
||||
fmt.Print(config_t.Messages.WEBRTC.ON_DATA_CHANNEL)
|
||||
})
|
||||
dc.OnMessage(func(msg webrtc.DataChannelMessage) {
|
||||
fmt.Print(config_t.Messages.WEBRTC.ON_MESSAGE)
|
||||
channelHandler(dc, msg.Data)
|
||||
})
|
||||
})
|
||||
|
||||
// Setting offer as remote description
|
||||
set_remote_description_error := p2p.SetRemoteDescription(offer_t.Offer)
|
||||
if set_remote_description_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WEBRTC.REMOTE_DESCRIPTION.ERROR,
|
||||
set_remote_description_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WEBRTC.REMOTE_DESCRIPTION.SUCCESS)
|
||||
}
|
||||
|
||||
// Generating answer
|
||||
answer_options := webrtc.AnswerOptions{
|
||||
OfferAnswerOptions: webrtc.OfferAnswerOptions{
|
||||
VoiceActivityDetection: false,
|
||||
},
|
||||
}
|
||||
answer, create_answer_error := p2p.CreateAnswer(&answer_options)
|
||||
if create_answer_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WEBRTC.ANSWER.ERROR, create_answer_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WEBRTC.ANSWER.SUCCESS)
|
||||
}
|
||||
|
||||
// Constructing answer, creating JSON from answer and sending it to the websocket
|
||||
answer_t := Answer{
|
||||
Type: "answer",
|
||||
Id: offer_t.Id,
|
||||
Answer: answer,
|
||||
}
|
||||
answer_msg, message_marshal_error := json.Marshal(answer_t)
|
||||
if message_marshal_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.JSON.MARSHAL.ERROR, message_marshal_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.JSON.MARSHAL.SUCCESS)
|
||||
}
|
||||
ws_write_error := conn.WriteMessage(websocket.TextMessage, answer_msg)
|
||||
if ws_write_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WS.WRITE.ERROR, ws_write_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WS.WRITE.SUCCESS)
|
||||
}
|
||||
// Setting answer as local description
|
||||
set_local_description_error := (*p2p).SetLocalDescription(answer)
|
||||
if set_local_description_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WEBRTC.LOCAL_DESCRIPTION.ERROR,
|
||||
set_local_description_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WEBRTC.LOCAL_DESCRIPTION.SUCCESS)
|
||||
}
|
||||
|
||||
// Storing the peer connection to the map
|
||||
(*peer_connections)[offer_t.Id] = p2p
|
||||
}
|
||||
|
||||
/*
|
||||
* This function gets:
|
||||
* peer_connections: pointer to map of tracked peer connections
|
||||
* raw: slice of bytes
|
||||
* Adds ICE candidate received from the signaling server.
|
||||
* The function does not return anything.
|
||||
*/
|
||||
func candidate(peer_connections *map[uuid.UUID]*webrtc.PeerConnection, raw []byte) {
|
||||
// Constructing candidate from received data
|
||||
var candidate_t Candidate
|
||||
message_unmarshal_error := json.Unmarshal(raw, &candidate_t)
|
||||
if message_unmarshal_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.JSON.UNMARSHAL.ERROR, message_unmarshal_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.JSON.UNMARSHAL.SUCCESS)
|
||||
}
|
||||
|
||||
// Getting peer instance from map
|
||||
p2p := (*peer_connections)[candidate_t.Id]
|
||||
// Adding candidate to peer connection
|
||||
add_ice_error := p2p.AddICECandidate(candidate_t.Candidate)
|
||||
if add_ice_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WEBRTC.ADD_CANDIDATE.ERROR, add_ice_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WEBRTC.ADD_CANDIDATE.SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function gets:
|
||||
* peer_connections: pointer to map of tracked peer connections
|
||||
* raw: slice of bytes
|
||||
* Terminates a peer connection.
|
||||
*/
|
||||
func terminate(peer_connections *map[uuid.UUID]*webrtc.PeerConnection, raw []byte) {
|
||||
var terminate_t Terminate
|
||||
message_unmarshal_error := json.Unmarshal(raw, &terminate_t)
|
||||
if message_unmarshal_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.JSON.UNMARSHAL.ERROR, message_unmarshal_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.JSON.UNMARSHAL.SUCCESS)
|
||||
}
|
||||
|
||||
// Getting peer instance from map and closing the connection
|
||||
p2p := (*peer_connections)[terminate_t.Id]
|
||||
p2p_close_error := p2p.Close()
|
||||
if p2p_close_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WEBRTC.CLOSE.ERROR, p2p_close_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WEBRTC.CLOSE.SUCCESS)
|
||||
}
|
||||
// Remove peer connection entry from map
|
||||
delete(*peer_connections, terminate_t.Id)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Attempting to read the config file
|
||||
raw_config, config_error := os.ReadFile("config.yml")
|
||||
if config_error != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not read config file.\n%v\n", config_error)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// Loading config file to config struct
|
||||
config_error = yaml.Unmarshal(raw_config, &config_t)
|
||||
if config_error != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not load config file.\n%v\n", config_error)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// Starting program
|
||||
fmt.Print(config_t.Messages.START)
|
||||
peer_connections := make(map[uuid.UUID]*webrtc.PeerConnection)
|
||||
|
||||
// Attempting to connect to the signaling server via WebSocket
|
||||
conn, _, dial_error := websocket.DefaultDialer.Dial(config_t.Config.SIGNALING_SERVER, nil)
|
||||
if dial_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WS.CONNECT.ERROR, dial_error)
|
||||
os.Exit(-1)
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WS.CONNECT.SUCCESS)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
ws_close_error := conn.Close()
|
||||
if ws_close_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WS.CLOSE.ERROR, ws_close_error)
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WS.CLOSE.SUCCESS)
|
||||
}
|
||||
}()
|
||||
|
||||
// Constructing the register message
|
||||
register_t := Register{
|
||||
Type: "register",
|
||||
Username: config_t.Config.AUTHENTICATION.USERNAME,
|
||||
Password: config_t.Config.AUTHENTICATION.PASSWORD,
|
||||
}
|
||||
// Encoding register struct to JSON
|
||||
register_msg, register_marshal_error := json.Marshal(register_t)
|
||||
if register_marshal_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.JSON.MARSHAL.ERROR, register_marshal_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.JSON.MARSHAL.SUCCESS)
|
||||
}
|
||||
|
||||
// Registering to the signaling server as normal user
|
||||
ws_write_error := conn.WriteMessage(websocket.TextMessage, register_msg)
|
||||
if ws_write_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WS.REGISTER.ERROR, ws_write_error)
|
||||
return
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WS.REGISTER.SUCCESS)
|
||||
}
|
||||
|
||||
for {
|
||||
// Reading message
|
||||
_, ws_msg, ws_read_error := conn.ReadMessage()
|
||||
if ws_read_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.WS.READ.ERROR, ws_read_error)
|
||||
os.Exit(-1)
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.WS.READ.SUCCESS)
|
||||
}
|
||||
|
||||
// Decoding as any-format JSON
|
||||
var parsed map[string]any
|
||||
message_unmarshal_error := json.Unmarshal(ws_msg, &parsed)
|
||||
if message_unmarshal_error != nil {
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.JSON.UNMARSHAL.ERROR, message_unmarshal_error)
|
||||
continue
|
||||
} else {
|
||||
fmt.Print(config_t.Messages.JSON.UNMARSHAL.SUCCESS)
|
||||
}
|
||||
|
||||
// Parsing message type
|
||||
msg_type, exists := parsed["type"].(string)
|
||||
if !exists {
|
||||
fmt.Fprint(os.Stderr, config_t.Messages.JSON.FORMAT.ERROR)
|
||||
continue
|
||||
} else {
|
||||
fmt.Printf(config_t.Messages.JSON.FORMAT.SUCCESS, msg_type)
|
||||
}
|
||||
|
||||
// Setting handler function for each message type
|
||||
switch msg_type {
|
||||
case "offer":
|
||||
fmt.Printf(config_t.Messages.JSON.TYPE.SUCCESS, msg_type)
|
||||
offer(conn, &peer_connections, ws_msg)
|
||||
case "candidate":
|
||||
fmt.Printf(config_t.Messages.JSON.TYPE.SUCCESS, msg_type)
|
||||
candidate(&peer_connections, ws_msg)
|
||||
case "terminate":
|
||||
fmt.Printf(config_t.Messages.JSON.TYPE.SUCCESS, msg_type)
|
||||
terminate(&peer_connections, ws_msg)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, config_t.Messages.JSON.TYPE.ERROR, msg_type)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user