Upload server
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
CC := go
|
||||||
|
NAME := server
|
||||||
|
OUT := bin/
|
||||||
|
|
||||||
|
all: main.go types.go
|
||||||
|
$(CC) build -o $(OUT)$(NAME) $^
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -r $(OUT)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
config:
|
||||||
|
SIGNALING_SERVER: "ws://"
|
||||||
|
PROXY_SERVER: "http://"
|
||||||
|
AUTHENTICATION:
|
||||||
|
USERNAME: "username"
|
||||||
|
PASSWORD: "password"
|
||||||
|
ICE_SERVERS:
|
||||||
|
- urls:
|
||||||
|
- "stun:stun.l.google.com:19302"
|
||||||
|
- urls:
|
||||||
|
- "turn:"
|
||||||
|
username: "turn_username"
|
||||||
|
credential: "turn_password"
|
||||||
|
messages:
|
||||||
|
START: "Loaded config. Starting...\n"
|
||||||
|
WS:
|
||||||
|
CONNECT:
|
||||||
|
SUCCESS: "Connected to WebSocket.\n"
|
||||||
|
ERROR: "Could not connect to WebSocket.\n %v\n\n"
|
||||||
|
CLOSE:
|
||||||
|
SUCCESS: "Disconnected from WebSocket.\n"
|
||||||
|
ERROR: "Could not close WebSocket connection.\n %v\n\n"
|
||||||
|
REGISTER:
|
||||||
|
SUCCESS: "Registered to WebSocket.\n"
|
||||||
|
ERROR: "Could not send register message.\n %v\n\n"
|
||||||
|
READ:
|
||||||
|
SUCCESS: "Received data from WebSocket.\n"
|
||||||
|
ERROR: "Could not read data from WebSocket.\n %v\n\n"
|
||||||
|
WRITE:
|
||||||
|
SUCCESS: "Sent data to WebSocket.\n"
|
||||||
|
ERROR: "Could not send data to WebSocket.\n %v\n\n"
|
||||||
|
JSON:
|
||||||
|
MARSHAL:
|
||||||
|
SUCCESS: "Converted struct to JSON string.\n"
|
||||||
|
ERROR: "Could not convert struct to JSON string.\n %v\n\n"
|
||||||
|
UNMARSHAL:
|
||||||
|
SUCCESS: "Converted JSON string to struct.\n"
|
||||||
|
ERROR: "Could not convert JSON string to struct.\n %v\n\n"
|
||||||
|
FORMAT:
|
||||||
|
SUCCESS: "Parsed JSON object type with value %s.\n"
|
||||||
|
ERROR: "Could not parse JSON object type.\n"
|
||||||
|
TYPE:
|
||||||
|
SUCCESS: "Handling JSON object as %s.\n"
|
||||||
|
ERROR: "Unknown JSON object %s.\n"
|
||||||
|
WEBRTC:
|
||||||
|
NEW:
|
||||||
|
SUCCESS: "Created WebRTC instance.\n"
|
||||||
|
ERROR: "Could not create WebRTC instance.\n %v\n\n"
|
||||||
|
CLOSE:
|
||||||
|
SUCCESS: "Closed P2P connection.\n"
|
||||||
|
ERROR: "Could not close P2P connection.\n %v\n\n"
|
||||||
|
ANSWER:
|
||||||
|
SUCCESS: "Created answer.\n"
|
||||||
|
ERROR: "Could not create answer.\n %v\n\n"
|
||||||
|
LOCAL_DESCRIPTION:
|
||||||
|
SUCCESS: "Set local description.\n"
|
||||||
|
ERROR: "Could not set local description.\n %v\n\n"
|
||||||
|
REMOTE_DESCRIPTION:
|
||||||
|
SUCCESS: "Set remote description.\n"
|
||||||
|
ERROR: "Could not set remote description.\n %v\n\n"
|
||||||
|
ADD_CANDIDATE:
|
||||||
|
SUCCESS: "Added ICE candidate.\n"
|
||||||
|
ERROR: "Could not add ICE candidate.\n %v\n\n"
|
||||||
|
SEND_DATA_CHANNEL:
|
||||||
|
SUCCESS: "Send data to data channel.\n"
|
||||||
|
ERROR: "Could not send data to data channel.\n %v\n\n"
|
||||||
|
ON_CANDIDATE: "Discovered ICE candidate.\n"
|
||||||
|
ON_DATA_CHANNEL: "Opened data channel.\n"
|
||||||
|
ON_MESSAGE: "Received data from data channel.\n"
|
||||||
|
HTTP:
|
||||||
|
READ:
|
||||||
|
SUCCESS: "Read HTTP response body.\n"
|
||||||
|
ERROR: "Could not read HTTP response body.\n %v\n\n"
|
||||||
|
REQUEST:
|
||||||
|
SUCCESS: "Created HTTP request.\n"
|
||||||
|
ERROR: "Could not create HTTP request.\n %v\n\n"
|
||||||
|
SEND_REQUEST:
|
||||||
|
SUCCESS: "Sent HTTP request.\n"
|
||||||
|
ERROR: "Could not send HTTP request.\n %v\n\n"
|
||||||
|
GOB:
|
||||||
|
ENCODE:
|
||||||
|
SUCCESS: "Encoded struct to buffer.\n"
|
||||||
|
ERROR: "Could not encode struct to buffer.\n %v\n\n"
|
||||||
|
DECODE:
|
||||||
|
SUCCESS: "Converted buffer to struct.\n"
|
||||||
|
ERROR: "Could not convert buffer to struct.\n %v\n\n"
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
module WebRTC
|
||||||
|
|
||||||
|
go 1.24.5
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/gorilla/websocket v1.5.3
|
||||||
|
github.com/pion/webrtc/v4 v4.1.3
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/pion/datachannel v1.5.10 // indirect
|
||||||
|
github.com/pion/dtls/v3 v3.0.6 // indirect
|
||||||
|
github.com/pion/ice/v4 v4.0.10 // indirect
|
||||||
|
github.com/pion/interceptor v0.1.40 // indirect
|
||||||
|
github.com/pion/logging v0.2.4 // indirect
|
||||||
|
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||||
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
|
github.com/pion/rtcp v1.2.15 // indirect
|
||||||
|
github.com/pion/rtp v1.8.20 // indirect
|
||||||
|
github.com/pion/sctp v1.8.39 // indirect
|
||||||
|
github.com/pion/sdp/v3 v3.0.14 // indirect
|
||||||
|
github.com/pion/srtp/v3 v3.0.6 // indirect
|
||||||
|
github.com/pion/stun/v3 v3.0.0 // indirect
|
||||||
|
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||||
|
github.com/pion/turn/v4 v4.0.0 // indirect
|
||||||
|
github.com/wlynxg/anet v0.0.5 // indirect
|
||||||
|
golang.org/x/crypto v0.33.0 // indirect
|
||||||
|
golang.org/x/net v0.35.0 // indirect
|
||||||
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
||||||
|
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
|
||||||
|
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
|
||||||
|
github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E=
|
||||||
|
github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU=
|
||||||
|
github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4=
|
||||||
|
github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
|
||||||
|
github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4=
|
||||||
|
github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic=
|
||||||
|
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
|
||||||
|
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
|
||||||
|
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
||||||
|
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
|
||||||
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
|
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
|
||||||
|
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
|
||||||
|
github.com/pion/rtp v1.8.20 h1:8zcyqohadZE8FCBeGdyEvHiclPIezcwRQH9zfapFyYI=
|
||||||
|
github.com/pion/rtp v1.8.20/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk=
|
||||||
|
github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE=
|
||||||
|
github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
|
||||||
|
github.com/pion/sdp/v3 v3.0.14 h1:1h7gBr9FhOWH5GjWWY5lcw/U85MtdcibTyt/o6RxRUI=
|
||||||
|
github.com/pion/sdp/v3 v3.0.14/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
|
||||||
|
github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4=
|
||||||
|
github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY=
|
||||||
|
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
||||||
|
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
||||||
|
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
||||||
|
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
||||||
|
github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
|
||||||
|
github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
|
||||||
|
github.com/pion/webrtc/v4 v4.1.3 h1:YZ67Boj9X/hk190jJZ8+HFGQ6DqSZ/fYP3sLAZv7c3c=
|
||||||
|
github.com/pion/webrtc/v4 v4.1.3/go.mod h1:rsq+zQ82ryfR9vbb0L1umPJ6Ogq7zm8mcn9fcGnxomM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||||
|
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||||
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
+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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* HTTP tunnel over WebRTC - Server types
|
||||||
|
* Author: Konstantinos Drakontidis
|
||||||
|
* Email: gedra100sh@gmail.com
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/pion/webrtc/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Terminate struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Id uuid.UUID `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Answer struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Id uuid.UUID `json:"id"`
|
||||||
|
Answer webrtc.SessionDescription `json:"answer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Offer struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Id uuid.UUID `json:"id"`
|
||||||
|
Offer webrtc.SessionDescription `json:"offer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Candidate struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Id uuid.UUID `json:"id"`
|
||||||
|
Candidate webrtc.ICECandidateInit `json:"candidate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Register struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Headers map[string][]string `json:"headers"`
|
||||||
|
Body []byte `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Status int `json:"status,omitempty"`
|
||||||
|
Headers map[string][]string `json:"headers,omitempty"`
|
||||||
|
Body []byte `json:"body,omitempty"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
SUCCESS string `yaml:"SUCCESS"`
|
||||||
|
ERROR string `yaml:"ERROR"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Config struct {
|
||||||
|
SIGNALING_SERVER string `yaml:"SIGNALING_SERVER"`
|
||||||
|
PROXY_SERVER string `yaml:"PROXY_SERVER"`
|
||||||
|
AUTHENTICATION struct {
|
||||||
|
USERNAME string `yaml:"USERNAME"`
|
||||||
|
PASSWORD string `yaml:"PASSWORD"`
|
||||||
|
} `yaml:"AUTHENTICATION"`
|
||||||
|
ICE_SERVERS []webrtc.ICEServer `yaml:"ICE_SERVERS"`
|
||||||
|
} `yaml:"config"`
|
||||||
|
Messages struct {
|
||||||
|
START string `yaml:"START"`
|
||||||
|
WS struct {
|
||||||
|
CONNECT Message `yaml:"CONNECT"`
|
||||||
|
CLOSE Message `yaml:"CLOSE"`
|
||||||
|
REGISTER Message `yaml:"REGISTER"`
|
||||||
|
READ Message `yaml:"READ"`
|
||||||
|
WRITE Message `yaml:"WRITE"`
|
||||||
|
} `yaml:"WS"`
|
||||||
|
JSON struct {
|
||||||
|
MARSHAL Message `yaml:"MARSHAL"`
|
||||||
|
UNMARSHAL Message `yaml:"UNMARSHAL"`
|
||||||
|
FORMAT Message `yaml:"FORMAT"`
|
||||||
|
TYPE Message `yaml:"TYPE"`
|
||||||
|
} `yaml:"JSON"`
|
||||||
|
WEBRTC struct {
|
||||||
|
NEW Message `yaml:"NEW"`
|
||||||
|
CLOSE Message `yaml:"CLOSE"`
|
||||||
|
ANSWER Message `yaml:"ANSWER"`
|
||||||
|
LOCAL_DESCRIPTION Message `yaml:"LOCAL_DESCRIPTION"`
|
||||||
|
REMOTE_DESCRIPTION Message `yaml:"REMOTE_DESCRIPTION"`
|
||||||
|
ADD_CANDIDATE Message `yaml:"ADD_CANDIDATE"`
|
||||||
|
SEND_DATA_CHANNEL Message `yaml:"SEND_DATA_CHANNEL"`
|
||||||
|
ON_CANDIDATE string `yaml:"ON_CANDIDATE"`
|
||||||
|
ON_DATA_CHANNEL string `yaml:"ON_DATA_CHANNEL"`
|
||||||
|
ON_MESSAGE string `yaml:"ON_MESSAGE"`
|
||||||
|
} `yaml:"WEBRTC"`
|
||||||
|
HTTP struct {
|
||||||
|
READ Message `yaml:"READ"`
|
||||||
|
REQUEST Message `yaml:"REQUEST"`
|
||||||
|
SEND_REQUEST Message `yaml:"SEND_REQUEST"`
|
||||||
|
} `yaml:"HTTP"`
|
||||||
|
GOB struct {
|
||||||
|
ENCODE Message `yaml:"ENCODE"`
|
||||||
|
DECODE Message `yaml:"DECODE"`
|
||||||
|
} `yaml:"GOB"`
|
||||||
|
} `yaml:"messages"`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user