add integrated packet dumper (#5488)
this allows to dump any incoming and outgoing packet, to disk, in pcapng format.
This commit is contained in:
@@ -71,21 +71,19 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: MediaMTX logs
|
label: MediaMTX logs
|
||||||
description: |
|
description: |
|
||||||
MediaMTX logs are often useful to identify the issue. If you think this is the case, set the parameter 'logLevel' to 'debug' and attach logs.
|
MediaMTX logs are often useful to identify the issue. If you think this is the case, set 'logLevel' to 'debug' and attach logs.
|
||||||
placeholder: Paste or drag the log file here
|
placeholder: Paste or drag the log file here
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: network
|
id: network
|
||||||
attributes:
|
attributes:
|
||||||
label: Network dump
|
label: Packet dump
|
||||||
description: |
|
description: |
|
||||||
If the bug arises when using MediaMTX with external hardware or software, the most helpful content you can provide is a dump of the data exchanged between the server and the target (network dump).
|
If the bug arises when using MediaMTX with external hardware or software, the most helpful information you can provide is a packet dump, that can be generated in this way:
|
||||||
|
|
||||||
That can be generated in this way:
|
1. In mediamtx.yml, set 'dumpPackets' to 'true'
|
||||||
1. Download wireshark (https://www.wireshark.org/)
|
2. Start the server and replicate the issue
|
||||||
2. Start capturing on the interface used for exchanging packets
|
3. Stop the server, find the generated .pcapng files in the current directory
|
||||||
* If the server and the external hardware or software are both installed on your pc, the interface is probably "loopback", otherwise it's the one of your network card.
|
4. Attach the pcapng files by dragging them here
|
||||||
3. Start the server and replicate the issue
|
|
||||||
4. Stop capturing, save the result in .pcap format
|
placeholder: Attach the pcapng files by dragging them here
|
||||||
5. Attach
|
|
||||||
placeholder: Attach the pcap file by dragging it here
|
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
sysLogPrefix:
|
sysLogPrefix:
|
||||||
type: string
|
type: string
|
||||||
|
dumpPackets:
|
||||||
|
type: boolean
|
||||||
readTimeout:
|
readTimeout:
|
||||||
type: string
|
type: string
|
||||||
writeTimeout:
|
writeTimeout:
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ require (
|
|||||||
github.com/alecthomas/kong v1.14.0
|
github.com/alecthomas/kong v1.14.0
|
||||||
github.com/asticode/go-astits v1.14.0
|
github.com/asticode/go-astits v1.14.0
|
||||||
github.com/bluenviron/gohlslib/v2 v2.2.5
|
github.com/bluenviron/gohlslib/v2 v2.2.5
|
||||||
github.com/bluenviron/gortmplib v0.2.1
|
github.com/bluenviron/gortmplib v0.2.2-0.20260218152940-e1378f261371
|
||||||
github.com/bluenviron/gortsplib/v5 v5.3.2-0.20260211202618-9aea8d6fc9d6
|
github.com/bluenviron/gortsplib/v5 v5.3.2-0.20260222121945-18b06189ef23
|
||||||
github.com/bluenviron/mediacommon/v2 v2.7.2
|
github.com/bluenviron/mediacommon/v2 v2.7.2
|
||||||
github.com/datarhei/gosrt v0.10.0
|
github.com/datarhei/gosrt v0.10.0
|
||||||
github.com/fsnotify/fsnotify v1.9.0
|
github.com/fsnotify/fsnotify v1.9.0
|
||||||
@@ -22,6 +22,7 @@ require (
|
|||||||
github.com/go-git/go-git/v5 v5.16.5
|
github.com/go-git/go-git/v5 v5.16.5
|
||||||
github.com/goccy/go-yaml v1.19.2
|
github.com/goccy/go-yaml v1.19.2
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
|
github.com/google/gopacket v1.1.19
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gookit/color v1.6.0
|
github.com/gookit/color v1.6.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYh
|
|||||||
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
|
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
|
||||||
github.com/bluenviron/gohlslib/v2 v2.2.5 h1:gQyVmymInXSwm7QgMBwaq6eZZAM0FaT+hX41iueMSyw=
|
github.com/bluenviron/gohlslib/v2 v2.2.5 h1:gQyVmymInXSwm7QgMBwaq6eZZAM0FaT+hX41iueMSyw=
|
||||||
github.com/bluenviron/gohlslib/v2 v2.2.5/go.mod h1:4ZQMhQ1m9SMFWfh65lptaz0YwHbRxDIza7mM9OZsydo=
|
github.com/bluenviron/gohlslib/v2 v2.2.5/go.mod h1:4ZQMhQ1m9SMFWfh65lptaz0YwHbRxDIza7mM9OZsydo=
|
||||||
github.com/bluenviron/gortmplib v0.2.1 h1:ep+Aq4WTqbXUsJqMMenaRth2ugG1u3cm/fG0jHoriYI=
|
github.com/bluenviron/gortmplib v0.2.2-0.20260218152940-e1378f261371 h1:xoizvABlBF+G2N3CO7IuV0/IrLzL964Av9IkkR6lE+s=
|
||||||
github.com/bluenviron/gortmplib v0.2.1/go.mod h1:AMqbShthdHCzF6OFjcJ4LHTBbeZ5jSfDZvZBW1zaToA=
|
github.com/bluenviron/gortmplib v0.2.2-0.20260218152940-e1378f261371/go.mod h1:HWpYpjVjl9pZlT3VRY9zciLrG1+JvYVY16VC6R7kB6M=
|
||||||
github.com/bluenviron/gortsplib/v5 v5.3.2-0.20260211202618-9aea8d6fc9d6 h1:Vnp5Lw784UU1jl7Xf0fcYkp2HMQDpOJpHQQRncD4ILg=
|
github.com/bluenviron/gortsplib/v5 v5.3.2-0.20260222121945-18b06189ef23 h1:ZtXURmQHjNIfG/raJyfoNgTAGniD1LKrrpk3Ze5O2VI=
|
||||||
github.com/bluenviron/gortsplib/v5 v5.3.2-0.20260211202618-9aea8d6fc9d6/go.mod h1:pMia36dVdPoRMBxV/T1Z2xYxwI8bFkkd7xc2WM5KsOM=
|
github.com/bluenviron/gortsplib/v5 v5.3.2-0.20260222121945-18b06189ef23/go.mod h1:k6NJcnpvsXDXGyVJpedGJ6K2ysA0xx9fiX9hA7pEua4=
|
||||||
github.com/bluenviron/mediacommon/v2 v2.7.2 h1:BA7X7YOKq4VC6Ygm5FIg2D2IYTt/Ud6T43anXkHbDE8=
|
github.com/bluenviron/mediacommon/v2 v2.7.2 h1:BA7X7YOKq4VC6Ygm5FIg2D2IYTt/Ud6T43anXkHbDE8=
|
||||||
github.com/bluenviron/mediacommon/v2 v2.7.2/go.mod h1:5V15TiOfeaNVmZPVuOqAwqQSWyvMV86/dijDKu5q9Zs=
|
github.com/bluenviron/mediacommon/v2 v2.7.2/go.mod h1:5V15TiOfeaNVmZPVuOqAwqQSWyvMV86/dijDKu5q9Zs=
|
||||||
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
||||||
@@ -104,6 +104,8 @@ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUv
|
|||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||||
|
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||||
github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef h1:xpF9fUHpoIrrjX24DURVKiwHcFpw19ndIs+FwTSMbno=
|
github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef h1:xpF9fUHpoIrrjX24DURVKiwHcFpw19ndIs+FwTSMbno=
|
||||||
github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
|
github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@@ -238,6 +240,7 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
|||||||
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
||||||
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
@@ -245,15 +248,20 @@ golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
|||||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -277,8 +285,10 @@ golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
|||||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
+12
-9
@@ -80,6 +80,7 @@ type API struct {
|
|||||||
Version string
|
Version string
|
||||||
Started time.Time
|
Started time.Time
|
||||||
Address string
|
Address string
|
||||||
|
DumpPackets bool
|
||||||
Encryption bool
|
Encryption bool
|
||||||
ServerKey string
|
ServerKey string
|
||||||
ServerCert string
|
ServerCert string
|
||||||
@@ -183,15 +184,17 @@ func (a *API) Initialize() error {
|
|||||||
group.DELETE("/recordings/deletesegment", a.onRecordingDeleteSegment)
|
group.DELETE("/recordings/deletesegment", a.onRecordingDeleteSegment)
|
||||||
|
|
||||||
a.httpServer = &httpp.Server{
|
a.httpServer = &httpp.Server{
|
||||||
Address: a.Address,
|
Address: a.Address,
|
||||||
AllowOrigins: a.AllowOrigins,
|
AllowOrigins: a.AllowOrigins,
|
||||||
ReadTimeout: time.Duration(a.ReadTimeout),
|
DumpPackets: a.DumpPackets,
|
||||||
WriteTimeout: time.Duration(a.WriteTimeout),
|
DumpPacketsPrefix: "api_server_conn",
|
||||||
Encryption: a.Encryption,
|
ReadTimeout: time.Duration(a.ReadTimeout),
|
||||||
ServerCert: a.ServerCert,
|
WriteTimeout: time.Duration(a.WriteTimeout),
|
||||||
ServerKey: a.ServerKey,
|
Encryption: a.Encryption,
|
||||||
Handler: router,
|
ServerCert: a.ServerCert,
|
||||||
Parent: a,
|
ServerKey: a.ServerKey,
|
||||||
|
Handler: router,
|
||||||
|
Parent: a,
|
||||||
}
|
}
|
||||||
err := a.httpServer.Initialize()
|
err := a.httpServer.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ type Conf struct {
|
|||||||
LogStructured bool `json:"logStructured"`
|
LogStructured bool `json:"logStructured"`
|
||||||
LogFile string `json:"logFile"`
|
LogFile string `json:"logFile"`
|
||||||
SysLogPrefix string `json:"sysLogPrefix"`
|
SysLogPrefix string `json:"sysLogPrefix"`
|
||||||
|
DumpPackets bool `json:"dumpPackets"`
|
||||||
ReadTimeout Duration `json:"readTimeout"`
|
ReadTimeout Duration `json:"readTimeout"`
|
||||||
WriteTimeout Duration `json:"writeTimeout"`
|
WriteTimeout Duration `json:"writeTimeout"`
|
||||||
ReadBufferCount *int `json:"readBufferCount,omitempty"` // deprecated
|
ReadBufferCount *int `json:"readBufferCount,omitempty"` // deprecated
|
||||||
|
|||||||
+23
-1
@@ -352,6 +352,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
p.metrics == nil {
|
p.metrics == nil {
|
||||||
i := &metrics.Metrics{
|
i := &metrics.Metrics{
|
||||||
Address: p.conf.MetricsAddress,
|
Address: p.conf.MetricsAddress,
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
Encryption: p.conf.MetricsEncryption,
|
Encryption: p.conf.MetricsEncryption,
|
||||||
ServerKey: p.conf.MetricsServerKey,
|
ServerKey: p.conf.MetricsServerKey,
|
||||||
ServerCert: p.conf.MetricsServerCert,
|
ServerCert: p.conf.MetricsServerCert,
|
||||||
@@ -373,6 +374,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
p.pprof == nil {
|
p.pprof == nil {
|
||||||
i := &pprof.PPROF{
|
i := &pprof.PPROF{
|
||||||
Address: p.conf.PPROFAddress,
|
Address: p.conf.PPROFAddress,
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
Encryption: p.conf.PPROFEncryption,
|
Encryption: p.conf.PPROFEncryption,
|
||||||
ServerKey: p.conf.PPROFServerKey,
|
ServerKey: p.conf.PPROFServerKey,
|
||||||
ServerCert: p.conf.PPROFServerCert,
|
ServerCert: p.conf.PPROFServerCert,
|
||||||
@@ -403,6 +405,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
p.playbackServer == nil {
|
p.playbackServer == nil {
|
||||||
i := &playback.Server{
|
i := &playback.Server{
|
||||||
Address: p.conf.PlaybackAddress,
|
Address: p.conf.PlaybackAddress,
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
Encryption: p.conf.PlaybackEncryption,
|
Encryption: p.conf.PlaybackEncryption,
|
||||||
ServerKey: p.conf.PlaybackServerKey,
|
ServerKey: p.conf.PlaybackServerKey,
|
||||||
ServerCert: p.conf.PlaybackServerCert,
|
ServerCert: p.conf.PlaybackServerCert,
|
||||||
@@ -426,7 +429,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
|
|
||||||
p.pathManager = &pathManager{
|
p.pathManager = &pathManager{
|
||||||
logLevel: p.conf.LogLevel,
|
logLevel: p.conf.LogLevel,
|
||||||
authManager: p.authManager,
|
dumpPackets: p.conf.DumpPackets,
|
||||||
rtspAddress: p.conf.RTSPAddress,
|
rtspAddress: p.conf.RTSPAddress,
|
||||||
readTimeout: p.conf.ReadTimeout,
|
readTimeout: p.conf.ReadTimeout,
|
||||||
writeTimeout: p.conf.WriteTimeout,
|
writeTimeout: p.conf.WriteTimeout,
|
||||||
@@ -434,6 +437,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
udpReadBufferSize: p.conf.UDPReadBufferSize,
|
udpReadBufferSize: p.conf.UDPReadBufferSize,
|
||||||
rtpMaxPayloadSize: rtpMaxPayloadSize,
|
rtpMaxPayloadSize: rtpMaxPayloadSize,
|
||||||
pathConfs: p.conf.Paths,
|
pathConfs: p.conf.Paths,
|
||||||
|
authManager: p.authManager,
|
||||||
externalCmdPool: p.externalCmdPool,
|
externalCmdPool: p.externalCmdPool,
|
||||||
metrics: p.metrics,
|
metrics: p.metrics,
|
||||||
parent: p,
|
parent: p,
|
||||||
@@ -453,6 +457,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
i := &rtsp.Server{
|
i := &rtsp.Server{
|
||||||
Address: p.conf.RTSPAddress,
|
Address: p.conf.RTSPAddress,
|
||||||
AuthMethods: p.conf.RTSPAuthMethods.ToAuthMethods(),
|
AuthMethods: p.conf.RTSPAuthMethods.ToAuthMethods(),
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
UDPReadBufferSize: udpReadBufferSize,
|
UDPReadBufferSize: udpReadBufferSize,
|
||||||
ReadTimeout: p.conf.ReadTimeout,
|
ReadTimeout: p.conf.ReadTimeout,
|
||||||
WriteTimeout: p.conf.WriteTimeout,
|
WriteTimeout: p.conf.WriteTimeout,
|
||||||
@@ -495,6 +500,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
i := &rtsp.Server{
|
i := &rtsp.Server{
|
||||||
Address: p.conf.RTSPSAddress,
|
Address: p.conf.RTSPSAddress,
|
||||||
AuthMethods: p.conf.RTSPAuthMethods.ToAuthMethods(),
|
AuthMethods: p.conf.RTSPAuthMethods.ToAuthMethods(),
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
UDPReadBufferSize: udpReadBufferSize,
|
UDPReadBufferSize: udpReadBufferSize,
|
||||||
ReadTimeout: p.conf.ReadTimeout,
|
ReadTimeout: p.conf.ReadTimeout,
|
||||||
WriteTimeout: p.conf.WriteTimeout,
|
WriteTimeout: p.conf.WriteTimeout,
|
||||||
@@ -531,6 +537,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
p.rtmpServer == nil {
|
p.rtmpServer == nil {
|
||||||
i := &rtmp.Server{
|
i := &rtmp.Server{
|
||||||
Address: p.conf.RTMPAddress,
|
Address: p.conf.RTMPAddress,
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
ReadTimeout: p.conf.ReadTimeout,
|
ReadTimeout: p.conf.ReadTimeout,
|
||||||
WriteTimeout: p.conf.WriteTimeout,
|
WriteTimeout: p.conf.WriteTimeout,
|
||||||
IsTLS: false,
|
IsTLS: false,
|
||||||
@@ -563,6 +570,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
IsTLS: true,
|
IsTLS: true,
|
||||||
ServerCert: p.conf.RTMPServerCert,
|
ServerCert: p.conf.RTMPServerCert,
|
||||||
ServerKey: p.conf.RTMPServerKey,
|
ServerKey: p.conf.RTMPServerKey,
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
RTSPAddress: p.conf.RTSPAddress,
|
RTSPAddress: p.conf.RTSPAddress,
|
||||||
RunOnConnect: p.conf.RunOnConnect,
|
RunOnConnect: p.conf.RunOnConnect,
|
||||||
RunOnConnectRestart: p.conf.RunOnConnectRestart,
|
RunOnConnectRestart: p.conf.RunOnConnectRestart,
|
||||||
@@ -583,6 +591,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
p.hlsServer == nil {
|
p.hlsServer == nil {
|
||||||
i := &hls.Server{
|
i := &hls.Server{
|
||||||
Address: p.conf.HLSAddress,
|
Address: p.conf.HLSAddress,
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
Encryption: p.conf.HLSEncryption,
|
Encryption: p.conf.HLSEncryption,
|
||||||
ServerKey: p.conf.HLSServerKey,
|
ServerKey: p.conf.HLSServerKey,
|
||||||
ServerCert: p.conf.HLSServerCert,
|
ServerCert: p.conf.HLSServerCert,
|
||||||
@@ -613,6 +622,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
p.webRTCServer == nil {
|
p.webRTCServer == nil {
|
||||||
i := &webrtc.Server{
|
i := &webrtc.Server{
|
||||||
Address: p.conf.WebRTCAddress,
|
Address: p.conf.WebRTCAddress,
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
Encryption: p.conf.WebRTCEncryption,
|
Encryption: p.conf.WebRTCEncryption,
|
||||||
ServerKey: p.conf.WebRTCServerKey,
|
ServerKey: p.conf.WebRTCServerKey,
|
||||||
ServerCert: p.conf.WebRTCServerCert,
|
ServerCert: p.conf.WebRTCServerCert,
|
||||||
@@ -671,6 +681,7 @@ func (p *Core) createResources(initial bool) error {
|
|||||||
Version: string(version),
|
Version: string(version),
|
||||||
Started: started,
|
Started: started,
|
||||||
Address: p.conf.APIAddress,
|
Address: p.conf.APIAddress,
|
||||||
|
DumpPackets: p.conf.DumpPackets,
|
||||||
Encryption: p.conf.APIEncryption,
|
Encryption: p.conf.APIEncryption,
|
||||||
ServerKey: p.conf.APIServerKey,
|
ServerKey: p.conf.APIServerKey,
|
||||||
ServerCert: p.conf.APIServerCert,
|
ServerCert: p.conf.APIServerCert,
|
||||||
@@ -742,6 +753,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
!reflect.DeepEqual(newConf.MetricsTrustedProxies, p.conf.MetricsTrustedProxies) ||
|
!reflect.DeepEqual(newConf.MetricsTrustedProxies, p.conf.MetricsTrustedProxies) ||
|
||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
closeAuthManager ||
|
closeAuthManager ||
|
||||||
closeLogger
|
closeLogger
|
||||||
|
|
||||||
@@ -755,6 +767,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
!reflect.DeepEqual(newConf.PPROFTrustedProxies, p.conf.PPROFTrustedProxies) ||
|
!reflect.DeepEqual(newConf.PPROFTrustedProxies, p.conf.PPROFTrustedProxies) ||
|
||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
closeAuthManager ||
|
closeAuthManager ||
|
||||||
closeLogger
|
closeLogger
|
||||||
|
|
||||||
@@ -775,6 +788,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
!reflect.DeepEqual(newConf.PlaybackTrustedProxies, p.conf.PlaybackTrustedProxies) ||
|
!reflect.DeepEqual(newConf.PlaybackTrustedProxies, p.conf.PlaybackTrustedProxies) ||
|
||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
closeAuthManager ||
|
closeAuthManager ||
|
||||||
closeLogger
|
closeLogger
|
||||||
if !closePlaybackServer && p.playbackServer != nil && !reflect.DeepEqual(newConf.Paths, p.conf.Paths) {
|
if !closePlaybackServer && p.playbackServer != nil && !reflect.DeepEqual(newConf.Paths, p.conf.Paths) {
|
||||||
@@ -783,6 +797,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
|
|
||||||
closePathManager := newConf == nil ||
|
closePathManager := newConf == nil ||
|
||||||
newConf.LogLevel != p.conf.LogLevel ||
|
newConf.LogLevel != p.conf.LogLevel ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
newConf.RTSPAddress != p.conf.RTSPAddress ||
|
newConf.RTSPAddress != p.conf.RTSPAddress ||
|
||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
@@ -803,6 +818,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
newConf.RTSPAddress != p.conf.RTSPAddress ||
|
newConf.RTSPAddress != p.conf.RTSPAddress ||
|
||||||
!reflect.DeepEqual(newConf.RTSPAuthMethods, p.conf.RTSPAuthMethods) ||
|
!reflect.DeepEqual(newConf.RTSPAuthMethods, p.conf.RTSPAuthMethods) ||
|
||||||
newConf.RTSPUDPReadBufferSize != p.conf.RTSPUDPReadBufferSize ||
|
newConf.RTSPUDPReadBufferSize != p.conf.RTSPUDPReadBufferSize ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
newConf.UDPReadBufferSize != p.conf.UDPReadBufferSize ||
|
newConf.UDPReadBufferSize != p.conf.UDPReadBufferSize ||
|
||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
@@ -826,6 +842,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
newConf.RTSPSAddress != p.conf.RTSPSAddress ||
|
newConf.RTSPSAddress != p.conf.RTSPSAddress ||
|
||||||
!reflect.DeepEqual(newConf.RTSPAuthMethods, p.conf.RTSPAuthMethods) ||
|
!reflect.DeepEqual(newConf.RTSPAuthMethods, p.conf.RTSPAuthMethods) ||
|
||||||
newConf.RTSPUDPReadBufferSize != p.conf.RTSPUDPReadBufferSize ||
|
newConf.RTSPUDPReadBufferSize != p.conf.RTSPUDPReadBufferSize ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
newConf.UDPReadBufferSize != p.conf.UDPReadBufferSize ||
|
newConf.UDPReadBufferSize != p.conf.UDPReadBufferSize ||
|
||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
@@ -845,6 +862,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
newConf.RTMP != p.conf.RTMP ||
|
newConf.RTMP != p.conf.RTMP ||
|
||||||
newConf.RTMPEncryption != p.conf.RTMPEncryption ||
|
newConf.RTMPEncryption != p.conf.RTMPEncryption ||
|
||||||
newConf.RTMPAddress != p.conf.RTMPAddress ||
|
newConf.RTMPAddress != p.conf.RTMPAddress ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
newConf.RTSPAddress != p.conf.RTSPAddress ||
|
newConf.RTSPAddress != p.conf.RTSPAddress ||
|
||||||
@@ -859,6 +877,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
newConf.RTMP != p.conf.RTMP ||
|
newConf.RTMP != p.conf.RTMP ||
|
||||||
newConf.RTMPEncryption != p.conf.RTMPEncryption ||
|
newConf.RTMPEncryption != p.conf.RTMPEncryption ||
|
||||||
newConf.RTMPSAddress != p.conf.RTMPSAddress ||
|
newConf.RTMPSAddress != p.conf.RTMPSAddress ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
newConf.RTMPServerCert != p.conf.RTMPServerCert ||
|
newConf.RTMPServerCert != p.conf.RTMPServerCert ||
|
||||||
@@ -889,6 +908,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
newConf.HLSMuxerCloseAfter != p.conf.HLSMuxerCloseAfter ||
|
newConf.HLSMuxerCloseAfter != p.conf.HLSMuxerCloseAfter ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
closePathManager ||
|
closePathManager ||
|
||||||
closeMetrics ||
|
closeMetrics ||
|
||||||
closeLogger
|
closeLogger
|
||||||
@@ -913,6 +933,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
newConf.WebRTCSTUNGatherTimeout != p.conf.WebRTCSTUNGatherTimeout ||
|
newConf.WebRTCSTUNGatherTimeout != p.conf.WebRTCSTUNGatherTimeout ||
|
||||||
newConf.WebRTCHandshakeTimeout != p.conf.WebRTCHandshakeTimeout ||
|
newConf.WebRTCHandshakeTimeout != p.conf.WebRTCHandshakeTimeout ||
|
||||||
newConf.WebRTCTrackGatherTimeout != p.conf.WebRTCTrackGatherTimeout ||
|
newConf.WebRTCTrackGatherTimeout != p.conf.WebRTCTrackGatherTimeout ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
closeMetrics ||
|
closeMetrics ||
|
||||||
closePathManager ||
|
closePathManager ||
|
||||||
closeLogger
|
closeLogger
|
||||||
@@ -940,6 +961,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
|
|||||||
!reflect.DeepEqual(newConf.APITrustedProxies, p.conf.APITrustedProxies) ||
|
!reflect.DeepEqual(newConf.APITrustedProxies, p.conf.APITrustedProxies) ||
|
||||||
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
newConf.ReadTimeout != p.conf.ReadTimeout ||
|
||||||
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
newConf.WriteTimeout != p.conf.WriteTimeout ||
|
||||||
|
newConf.DumpPackets != p.conf.DumpPackets ||
|
||||||
closeAuthManager ||
|
closeAuthManager ||
|
||||||
closePathManager ||
|
closePathManager ||
|
||||||
closeRTSPServer ||
|
closeRTSPServer ||
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ type pathAPIPathsGetReq struct {
|
|||||||
type path struct {
|
type path struct {
|
||||||
parentCtx context.Context
|
parentCtx context.Context
|
||||||
logLevel conf.LogLevel
|
logLevel conf.LogLevel
|
||||||
|
dumpPackets bool
|
||||||
rtspAddress string
|
rtspAddress string
|
||||||
readTimeout conf.Duration
|
readTimeout conf.Duration
|
||||||
writeTimeout conf.Duration
|
writeTimeout conf.Duration
|
||||||
@@ -193,6 +194,7 @@ func (pa *path) run() {
|
|||||||
pa.source = &staticsources.Handler{
|
pa.source = &staticsources.Handler{
|
||||||
Conf: pa.conf,
|
Conf: pa.conf,
|
||||||
LogLevel: pa.logLevel,
|
LogLevel: pa.logLevel,
|
||||||
|
DumpPackets: pa.dumpPackets,
|
||||||
ReadTimeout: pa.readTimeout,
|
ReadTimeout: pa.readTimeout,
|
||||||
WriteTimeout: pa.writeTimeout,
|
WriteTimeout: pa.writeTimeout,
|
||||||
WriteQueueSize: pa.writeQueueSize,
|
WriteQueueSize: pa.writeQueueSize,
|
||||||
|
|||||||
@@ -71,14 +71,15 @@ type pathManagerParent interface {
|
|||||||
|
|
||||||
type pathManager struct {
|
type pathManager struct {
|
||||||
logLevel conf.LogLevel
|
logLevel conf.LogLevel
|
||||||
authManager pathManagerAuthManager
|
|
||||||
rtspAddress string
|
rtspAddress string
|
||||||
|
dumpPackets bool
|
||||||
readTimeout conf.Duration
|
readTimeout conf.Duration
|
||||||
writeTimeout conf.Duration
|
writeTimeout conf.Duration
|
||||||
writeQueueSize int
|
writeQueueSize int
|
||||||
udpReadBufferSize uint
|
udpReadBufferSize uint
|
||||||
rtpMaxPayloadSize int
|
rtpMaxPayloadSize int
|
||||||
pathConfs map[string]*conf.Path
|
pathConfs map[string]*conf.Path
|
||||||
|
authManager pathManagerAuthManager
|
||||||
externalCmdPool *externalcmd.Pool
|
externalCmdPool *externalcmd.Pool
|
||||||
metrics *metrics.Metrics
|
metrics *metrics.Metrics
|
||||||
parent pathManagerParent
|
parent pathManagerParent
|
||||||
@@ -443,6 +444,7 @@ func (pm *pathManager) createPath(
|
|||||||
pa := &path{
|
pa := &path{
|
||||||
parentCtx: pm.ctx,
|
parentCtx: pm.ctx,
|
||||||
logLevel: pm.logLevel,
|
logLevel: pm.logLevel,
|
||||||
|
dumpPackets: pm.dumpPackets,
|
||||||
rtspAddress: pm.rtspAddress,
|
rtspAddress: pm.rtspAddress,
|
||||||
readTimeout: pm.readTimeout,
|
readTimeout: pm.readTimeout,
|
||||||
writeTimeout: pm.writeTimeout,
|
writeTimeout: pm.writeTimeout,
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ type metricsParent interface {
|
|||||||
// Metrics is a metrics provider.
|
// Metrics is a metrics provider.
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
Address string
|
Address string
|
||||||
|
DumpPackets bool
|
||||||
Encryption bool
|
Encryption bool
|
||||||
ServerKey string
|
ServerKey string
|
||||||
ServerCert string
|
ServerCert string
|
||||||
@@ -104,15 +105,17 @@ func (m *Metrics) Initialize() error {
|
|||||||
router.GET("/metrics", m.onMetrics)
|
router.GET("/metrics", m.onMetrics)
|
||||||
|
|
||||||
m.httpServer = &httpp.Server{
|
m.httpServer = &httpp.Server{
|
||||||
Address: m.Address,
|
Address: m.Address,
|
||||||
AllowOrigins: m.AllowOrigins,
|
AllowOrigins: m.AllowOrigins,
|
||||||
ReadTimeout: time.Duration(m.ReadTimeout),
|
DumpPackets: m.DumpPackets,
|
||||||
WriteTimeout: time.Duration(m.WriteTimeout),
|
DumpPacketsPrefix: "metrics_server_conn",
|
||||||
Encryption: m.Encryption,
|
ReadTimeout: time.Duration(m.ReadTimeout),
|
||||||
ServerCert: m.ServerCert,
|
WriteTimeout: time.Duration(m.WriteTimeout),
|
||||||
ServerKey: m.ServerKey,
|
Encryption: m.Encryption,
|
||||||
Handler: router,
|
ServerCert: m.ServerCert,
|
||||||
Parent: m,
|
ServerKey: m.ServerKey,
|
||||||
|
Handler: router,
|
||||||
|
Parent: m,
|
||||||
}
|
}
|
||||||
err := m.httpServer.Initialize()
|
err := m.httpServer.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,258 @@
|
|||||||
|
// Package packetdumper provides utilities to dump packets to disk.
|
||||||
|
package packetdumper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/google/gopacket/pcapgo"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ net.Conn = (*Conn)(nil)
|
||||||
|
|
||||||
|
type direction int
|
||||||
|
|
||||||
|
const (
|
||||||
|
dirRead direction = iota
|
||||||
|
dirWrite
|
||||||
|
dirHandshake
|
||||||
|
)
|
||||||
|
|
||||||
|
type dumpEntry struct {
|
||||||
|
ntp time.Time
|
||||||
|
data []byte
|
||||||
|
direction direction
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn is a wrapper around net.Conn that dumps packets to disk.
|
||||||
|
type Conn struct {
|
||||||
|
Prefix string
|
||||||
|
Conn net.Conn
|
||||||
|
ServerSide bool
|
||||||
|
|
||||||
|
f *os.File
|
||||||
|
pw *pcapgo.NgWriter
|
||||||
|
once sync.Once
|
||||||
|
|
||||||
|
queue chan dumpEntry
|
||||||
|
terminated chan struct{}
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes Conn.
|
||||||
|
func (c *Conn) Initialize() error {
|
||||||
|
var err error
|
||||||
|
c.f, err = os.Create(fmt.Sprintf("%s_%d_%s.pcapng", c.Prefix, time.Now().UnixNano(), uuid.New().String()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.pw, err = pcapgo.NewNgWriter(c.f, layers.LinkTypeEthernet)
|
||||||
|
if err != nil {
|
||||||
|
c.f.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.queue = make(chan dumpEntry, 64)
|
||||||
|
c.terminated = make(chan struct{})
|
||||||
|
c.done = make(chan struct{})
|
||||||
|
|
||||||
|
go c.run()
|
||||||
|
|
||||||
|
c.enqueue(dumpEntry{ntp: time.Now(), direction: dirHandshake})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements net.Conn.
|
||||||
|
func (c *Conn) Close() error {
|
||||||
|
c.once.Do(func() {
|
||||||
|
close(c.terminated)
|
||||||
|
})
|
||||||
|
<-c.done
|
||||||
|
return c.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) run() {
|
||||||
|
defer close(c.done)
|
||||||
|
defer c.f.Close()
|
||||||
|
|
||||||
|
local := c.Conn.LocalAddr().(*net.TCPAddr)
|
||||||
|
remote := c.Conn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
|
||||||
|
nextLocalSequence := uint32(1000)
|
||||||
|
nextRemoteSequence := uint32(2000)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e := <-c.queue:
|
||||||
|
c.processEntry(e, local, remote, &nextLocalSequence, &nextRemoteSequence)
|
||||||
|
|
||||||
|
case <-c.terminated:
|
||||||
|
// Drain anything already in the queue before exiting.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e := <-c.queue:
|
||||||
|
c.processEntry(e, local, remote, &nextLocalSequence, &nextRemoteSequence)
|
||||||
|
default:
|
||||||
|
c.pw.Flush() //nolint:errcheck
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) processEntry(
|
||||||
|
e dumpEntry,
|
||||||
|
local, remote *net.TCPAddr,
|
||||||
|
nextLocalSequence, nextRemoteSequence *uint32,
|
||||||
|
) {
|
||||||
|
switch e.direction {
|
||||||
|
case dirHandshake:
|
||||||
|
clientAddr, serverAddr := local, remote // client side: local initiates
|
||||||
|
clientSeq, serverSeq := nextLocalSequence, nextRemoteSequence
|
||||||
|
if c.ServerSide {
|
||||||
|
clientAddr, serverAddr = remote, local // server side: remote initiated
|
||||||
|
clientSeq, serverSeq = nextRemoteSequence, nextLocalSequence
|
||||||
|
}
|
||||||
|
|
||||||
|
// SYN (client -> server)
|
||||||
|
c.writePacket(e.ntp, clientAddr, serverAddr,
|
||||||
|
layers.TCP{SYN: true, Window: 65535, Seq: *clientSeq, Ack: 0}, nil)
|
||||||
|
*clientSeq++
|
||||||
|
|
||||||
|
// SYN-ACK (server -> client)
|
||||||
|
c.writePacket(e.ntp, serverAddr, clientAddr,
|
||||||
|
layers.TCP{SYN: true, ACK: true, Window: 65535, Seq: *serverSeq, Ack: *clientSeq}, nil)
|
||||||
|
*serverSeq++
|
||||||
|
|
||||||
|
// ACK (client -> server)
|
||||||
|
c.writePacket(e.ntp, clientAddr, serverAddr,
|
||||||
|
layers.TCP{ACK: true, Window: 65535, Seq: *clientSeq, Ack: *serverSeq}, nil)
|
||||||
|
|
||||||
|
case dirRead:
|
||||||
|
tcpFlags := layers.TCP{
|
||||||
|
PSH: true,
|
||||||
|
ACK: true,
|
||||||
|
Window: 14600,
|
||||||
|
Seq: *nextRemoteSequence,
|
||||||
|
Ack: *nextLocalSequence,
|
||||||
|
}
|
||||||
|
c.writePacket(e.ntp, remote, local, tcpFlags, e.data)
|
||||||
|
*nextRemoteSequence += uint32(len(e.data))
|
||||||
|
|
||||||
|
case dirWrite:
|
||||||
|
tcpFlags := layers.TCP{
|
||||||
|
PSH: true,
|
||||||
|
ACK: true,
|
||||||
|
Window: 14600,
|
||||||
|
Seq: *nextLocalSequence,
|
||||||
|
Ack: *nextRemoteSequence,
|
||||||
|
}
|
||||||
|
c.writePacket(e.ntp, local, remote, tcpFlags, e.data)
|
||||||
|
*nextLocalSequence += uint32(len(e.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) writePacket(
|
||||||
|
ntp time.Time,
|
||||||
|
src, dst *net.TCPAddr,
|
||||||
|
tcpFlags layers.TCP,
|
||||||
|
payload []byte,
|
||||||
|
) {
|
||||||
|
eth := &layers.Ethernet{
|
||||||
|
SrcMAC: net.HardwareAddr{0, 0, 0, 0, 0, 0},
|
||||||
|
DstMAC: net.HardwareAddr{0, 0, 0, 0, 0, 0},
|
||||||
|
EthernetType: layers.EthernetTypeIPv6,
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6 := &layers.IPv6{
|
||||||
|
Version: 6,
|
||||||
|
SrcIP: src.IP.To16(),
|
||||||
|
DstIP: dst.IP.To16(),
|
||||||
|
NextHeader: layers.IPProtocolTCP,
|
||||||
|
HopLimit: 64,
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp := &layers.TCP{
|
||||||
|
SrcPort: layers.TCPPort(src.Port),
|
||||||
|
DstPort: layers.TCPPort(dst.Port),
|
||||||
|
Seq: tcpFlags.Seq,
|
||||||
|
Ack: tcpFlags.Ack,
|
||||||
|
Window: tcpFlags.Window,
|
||||||
|
SYN: tcpFlags.SYN,
|
||||||
|
ACK: tcpFlags.ACK,
|
||||||
|
PSH: tcpFlags.PSH,
|
||||||
|
FIN: tcpFlags.FIN,
|
||||||
|
}
|
||||||
|
tcp.SetNetworkLayerForChecksum(ipv6) //nolint:errcheck
|
||||||
|
|
||||||
|
buf := gopacket.NewSerializeBuffer()
|
||||||
|
opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
|
||||||
|
gopacket.SerializeLayers(buf, opts, eth, ipv6, tcp, gopacket.Payload(payload)) //nolint:errcheck
|
||||||
|
|
||||||
|
raw := buf.Bytes()
|
||||||
|
|
||||||
|
c.pw.WritePacket(gopacket.CaptureInfo{ //nolint:errcheck
|
||||||
|
Timestamp: ntp,
|
||||||
|
CaptureLength: len(raw),
|
||||||
|
Length: len(raw),
|
||||||
|
}, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) enqueue(e dumpEntry) {
|
||||||
|
select {
|
||||||
|
case c.queue <- e:
|
||||||
|
case <-c.terminated:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = c.Conn.Read(p)
|
||||||
|
|
||||||
|
if n != 0 {
|
||||||
|
c.enqueue(dumpEntry{
|
||||||
|
ntp: time.Now(),
|
||||||
|
data: append([]byte(nil), p[:n]...),
|
||||||
|
direction: dirRead,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Write(p []byte) (n int, err error) {
|
||||||
|
n, err = c.Conn.Write(p)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
c.enqueue(dumpEntry{
|
||||||
|
ntp: time.Now(),
|
||||||
|
data: append([]byte(nil), p...),
|
||||||
|
direction: dirWrite,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr implements net.Conn.
|
||||||
|
func (c *Conn) LocalAddr() net.Addr { return c.Conn.LocalAddr() }
|
||||||
|
|
||||||
|
// RemoteAddr implements net.Conn.
|
||||||
|
func (c *Conn) RemoteAddr() net.Addr { return c.Conn.RemoteAddr() }
|
||||||
|
|
||||||
|
// SetDeadline implements net.Conn.
|
||||||
|
func (c *Conn) SetDeadline(t time.Time) error { return c.Conn.SetDeadline(t) }
|
||||||
|
|
||||||
|
// SetReadDeadline implements net.Conn.
|
||||||
|
func (c *Conn) SetReadDeadline(t time.Time) error { return c.Conn.SetReadDeadline(t) }
|
||||||
|
|
||||||
|
// SetWriteDeadline implements net.Conn.
|
||||||
|
func (c *Conn) SetWriteDeadline(t time.Time) error { return c.Conn.SetWriteDeadline(t) }
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
package packetdumper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// startTCPPair dials a local TCP listener and returns both ends of the connection.
|
||||||
|
func startTCPPair(t *testing.T) (client, server net.Conn) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
serverCh := make(chan net.Conn, 1)
|
||||||
|
go func() {
|
||||||
|
conn, err2 := ln.Accept()
|
||||||
|
if err2 == nil {
|
||||||
|
serverCh <- conn
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
client, err = net.Dial("tcp", ln.Addr().String())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Cleanup(func() { ln.Close() })
|
||||||
|
|
||||||
|
select {
|
||||||
|
case server = <-serverCh:
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
t.Fatal("timed out waiting for server connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, server
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanupPcapng(t *testing.T, prefix string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
matches, err := filepath.Glob(prefix + "_*.pcapng")
|
||||||
|
require.NoError(t, err, "glob for pcapng files")
|
||||||
|
require.NotEmpty(t, matches, "expected at least one pcapng file to have been created")
|
||||||
|
|
||||||
|
for _, f := range matches {
|
||||||
|
require.NoError(t, os.Remove(f), "removing pcapng file %s", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnInitialize_CreatesFile(t *testing.T) {
|
||||||
|
client, server := startTCPPair(t)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &Conn{Prefix: prefix, Conn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapng(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnWrite(t *testing.T) {
|
||||||
|
client, server := startTCPPair(t)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &Conn{Prefix: prefix, Conn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapng(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
n, err := c.Write([]byte("hello world"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 11, n)
|
||||||
|
|
||||||
|
buf := make([]byte, 11)
|
||||||
|
_, err = io.ReadFull(server, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("hello world"), buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnRead(t *testing.T) {
|
||||||
|
client, server := startTCPPair(t)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &Conn{Prefix: prefix, Conn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapng(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
_, err := server.Write([]byte("incoming data"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
n, err := c.Read(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("incoming data"), buf[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnServerSide(t *testing.T) {
|
||||||
|
client, server := startTCPPair(t)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &Conn{Prefix: prefix, Conn: server, ServerSide: true}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapng(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
n, err := c.Write([]byte("server response"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 15, n)
|
||||||
|
|
||||||
|
buf := make([]byte, 15)
|
||||||
|
_, err = io.ReadFull(client, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("server response"), buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnMultipleWriteRead(t *testing.T) {
|
||||||
|
client, server := startTCPPair(t)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &Conn{Prefix: prefix, Conn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapng(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
for _, msg := range []string{"foo", "bar", "baz"} {
|
||||||
|
n, err := c.Write([]byte(msg))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(msg), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, len("foobarbaz"))
|
||||||
|
_, err := io.ReadFull(server, buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("foobarbaz"), buf)
|
||||||
|
|
||||||
|
_, err = server.Write([]byte("abcde"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = server.Write([]byte("fghij"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
readBuf := make([]byte, 10)
|
||||||
|
_, err = io.ReadFull(c, readBuf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("abcdefghij"), readBuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnCloseIdempotent(t *testing.T) {
|
||||||
|
client, server := startTCPPair(t)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &Conn{Prefix: prefix, Conn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapng(t, prefix)
|
||||||
|
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnDelegatesAddrMethods(t *testing.T) {
|
||||||
|
client, server := startTCPPair(t)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &Conn{Prefix: prefix, Conn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapng(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
require.Equal(t, client.LocalAddr(), c.LocalAddr())
|
||||||
|
require.Equal(t, client.RemoteAddr(), c.RemoteAddr())
|
||||||
|
|
||||||
|
require.NoError(t, c.SetDeadline(time.Now().Add(time.Second)))
|
||||||
|
require.NoError(t, c.SetReadDeadline(time.Now().Add(time.Second)))
|
||||||
|
require.NoError(t, c.SetWriteDeadline(time.Now().Add(time.Second)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package packetdumper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DialContext is a wrapper around net.Dialer.DialContext that dumps packets to disk.
|
||||||
|
type DialContext struct {
|
||||||
|
Prefix string
|
||||||
|
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do mimics net.Dialer.DialContext.
|
||||||
|
func (d *DialContext) Do(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
conn, err := d.DialContext(ctx, network, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &Conn{
|
||||||
|
Prefix: d.Prefix,
|
||||||
|
Conn: conn,
|
||||||
|
}
|
||||||
|
err = c.Initialize()
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package packetdumper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listen is a wrapper around net.Listen that dumps packets to disk.
|
||||||
|
type Listen struct {
|
||||||
|
Prefix string
|
||||||
|
Listen func(network, address string) (net.Listener, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do mimics net.Listen.
|
||||||
|
func (l *Listen) Do(network, address string) (net.Listener, error) {
|
||||||
|
ln, err := l.Listen(network, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Listener{
|
||||||
|
Prefix: l.Prefix,
|
||||||
|
Listener: ln,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package packetdumper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListenPacket is a wrapper around net.ListenPacket that dumps packets to disk.
|
||||||
|
type ListenPacket struct {
|
||||||
|
Prefix string
|
||||||
|
ListenPacket func(network, address string) (net.PacketConn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do mimics net.ListenPacket
|
||||||
|
func (l *ListenPacket) Do(network, address string) (net.PacketConn, error) {
|
||||||
|
pc, err := l.ListenPacket(network, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &PacketConn{
|
||||||
|
Prefix: l.Prefix,
|
||||||
|
PacketConn: pc,
|
||||||
|
}
|
||||||
|
err = d.Initialize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package packetdumper
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
var _ net.Listener = (*Listener)(nil)
|
||||||
|
|
||||||
|
// Listener is a wrapper around net.Listener that dumps packets to disk.
|
||||||
|
type Listener struct {
|
||||||
|
Prefix string
|
||||||
|
Listener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept implements net.Listener.
|
||||||
|
func (l *Listener) Accept() (net.Conn, error) {
|
||||||
|
conn, err := l.Listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cd := &Conn{
|
||||||
|
Prefix: l.Prefix,
|
||||||
|
Conn: conn,
|
||||||
|
ServerSide: true,
|
||||||
|
}
|
||||||
|
err = cd.Initialize()
|
||||||
|
if err != nil {
|
||||||
|
conn.Close() //nolint:errcheck
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements net.Listener.
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
return l.Listener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr implements net.Listener.
|
||||||
|
func (l *Listener) Addr() net.Addr {
|
||||||
|
return l.Listener.Addr()
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
package packetdumper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/google/gopacket/pcapgo"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ net.PacketConn = (*PacketConn)(nil)
|
||||||
|
|
||||||
|
type extendedPacketConn interface {
|
||||||
|
net.PacketConn
|
||||||
|
SetReadBuffer(bytes int) error
|
||||||
|
SyscallConn() (syscall.RawConn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type packetDumpEntry struct {
|
||||||
|
ntp time.Time
|
||||||
|
data []byte
|
||||||
|
src, dst *net.UDPAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketConn is a wrapper around net.PacketConn that dumps packets to disk.
|
||||||
|
type PacketConn struct {
|
||||||
|
Prefix string
|
||||||
|
PacketConn net.PacketConn
|
||||||
|
|
||||||
|
f *os.File
|
||||||
|
pw *pcapgo.NgWriter
|
||||||
|
once sync.Once
|
||||||
|
|
||||||
|
queue chan packetDumpEntry
|
||||||
|
terminated chan struct{}
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes PacketConn.
|
||||||
|
func (c *PacketConn) Initialize() error {
|
||||||
|
var err error
|
||||||
|
c.f, err = os.Create(fmt.Sprintf("%s_%d_%s.pcapng", c.Prefix, time.Now().UnixNano(), uuid.New().String()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.pw, err = pcapgo.NewNgWriter(c.f, layers.LinkTypeEthernet)
|
||||||
|
if err != nil {
|
||||||
|
c.f.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.queue = make(chan packetDumpEntry, 64)
|
||||||
|
c.terminated = make(chan struct{})
|
||||||
|
c.done = make(chan struct{})
|
||||||
|
|
||||||
|
go c.run()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements net.PacketConn.
|
||||||
|
func (c *PacketConn) Close() error {
|
||||||
|
c.once.Do(func() {
|
||||||
|
close(c.terminated)
|
||||||
|
})
|
||||||
|
<-c.done
|
||||||
|
return c.PacketConn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PacketConn) run() {
|
||||||
|
defer close(c.done)
|
||||||
|
defer c.f.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e := <-c.queue:
|
||||||
|
c.writePacket(e.ntp, e.src, e.dst, e.data)
|
||||||
|
|
||||||
|
case <-c.terminated:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e := <-c.queue:
|
||||||
|
c.writePacket(e.ntp, e.src, e.dst, e.data)
|
||||||
|
default:
|
||||||
|
c.pw.Flush() //nolint:errcheck
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PacketConn) writePacket(ntp time.Time, src, dst *net.UDPAddr, payload []byte) {
|
||||||
|
eth := &layers.Ethernet{
|
||||||
|
SrcMAC: net.HardwareAddr{0, 0, 0, 0, 0, 0},
|
||||||
|
DstMAC: net.HardwareAddr{0, 0, 0, 0, 0, 0},
|
||||||
|
EthernetType: layers.EthernetTypeIPv6,
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6 := &layers.IPv6{
|
||||||
|
Version: 6,
|
||||||
|
SrcIP: src.IP.To16(),
|
||||||
|
DstIP: dst.IP.To16(),
|
||||||
|
NextHeader: layers.IPProtocolUDP,
|
||||||
|
HopLimit: 64,
|
||||||
|
}
|
||||||
|
|
||||||
|
udp := &layers.UDP{
|
||||||
|
SrcPort: layers.UDPPort(src.Port),
|
||||||
|
DstPort: layers.UDPPort(dst.Port),
|
||||||
|
}
|
||||||
|
udp.SetNetworkLayerForChecksum(ipv6) //nolint:errcheck
|
||||||
|
|
||||||
|
buf := gopacket.NewSerializeBuffer()
|
||||||
|
opts := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
|
||||||
|
gopacket.SerializeLayers(buf, opts, eth, ipv6, udp, gopacket.Payload(payload)) //nolint:errcheck
|
||||||
|
|
||||||
|
raw := buf.Bytes()
|
||||||
|
c.pw.WritePacket(gopacket.CaptureInfo{ //nolint:errcheck
|
||||||
|
Timestamp: ntp,
|
||||||
|
CaptureLength: len(raw),
|
||||||
|
Length: len(raw),
|
||||||
|
}, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PacketConn) enqueue(e packetDumpEntry) {
|
||||||
|
select {
|
||||||
|
case c.queue <- e:
|
||||||
|
case <-c.terminated:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrom implements net.PacketConn.
|
||||||
|
func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
n, addr, err = c.PacketConn.ReadFrom(p)
|
||||||
|
|
||||||
|
if n != 0 {
|
||||||
|
local := c.PacketConn.LocalAddr().(*net.UDPAddr)
|
||||||
|
remote := addr.(*net.UDPAddr)
|
||||||
|
|
||||||
|
c.enqueue(packetDumpEntry{
|
||||||
|
ntp: time.Now(),
|
||||||
|
data: append([]byte(nil), p[:n]...),
|
||||||
|
src: remote,
|
||||||
|
dst: local,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo implements net.PacketConn.
|
||||||
|
func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
n, err = c.PacketConn.WriteTo(p, addr)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
local := c.PacketConn.LocalAddr().(*net.UDPAddr)
|
||||||
|
remote := addr.(*net.UDPAddr)
|
||||||
|
|
||||||
|
c.enqueue(packetDumpEntry{
|
||||||
|
ntp: time.Now(),
|
||||||
|
data: append([]byte(nil), p...),
|
||||||
|
src: local,
|
||||||
|
dst: remote,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr implements net.PacketConn.
|
||||||
|
func (c *PacketConn) LocalAddr() net.Addr { return c.PacketConn.LocalAddr() }
|
||||||
|
|
||||||
|
// SetDeadline implements net.PacketConn.
|
||||||
|
func (c *PacketConn) SetDeadline(t time.Time) error { return c.PacketConn.SetDeadline(t) }
|
||||||
|
|
||||||
|
// SetReadDeadline implements net.PacketConn.
|
||||||
|
func (c *PacketConn) SetReadDeadline(t time.Time) error { return c.PacketConn.SetReadDeadline(t) }
|
||||||
|
|
||||||
|
// SetWriteDeadline implements net.PacketConn.
|
||||||
|
func (c *PacketConn) SetWriteDeadline(t time.Time) error { return c.PacketConn.SetWriteDeadline(t) }
|
||||||
|
|
||||||
|
// SetReadBuffer implements extendedPacketConn.
|
||||||
|
func (c *PacketConn) SetReadBuffer(bytes int) error {
|
||||||
|
return c.PacketConn.(extendedPacketConn).SetReadBuffer(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyscallConn implements extendedPacketConn.
|
||||||
|
func (c *PacketConn) SyscallConn() (syscall.RawConn, error) {
|
||||||
|
return c.PacketConn.(extendedPacketConn).SyscallConn()
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
package packetdumper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// startUDPPair creates a pair of UDP connections and returns both ends.
|
||||||
|
func startUDPPair(t *testing.T) (client, server *net.UDPConn) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
server, err = net.ListenUDP("udp", serverAddr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
clientAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
client, err = net.ListenUDP("udp", clientAddr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
server.Close() //nolint:errcheck
|
||||||
|
client.Close() //nolint:errcheck
|
||||||
|
})
|
||||||
|
|
||||||
|
return client, server
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanupPcapngPacket(t *testing.T, prefix string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
matches, err := filepath.Glob(prefix + "_*.pcapng")
|
||||||
|
require.NoError(t, err, "glob for pcapng files")
|
||||||
|
require.NotEmpty(t, matches, "expected at least one pcapng file to have been created")
|
||||||
|
|
||||||
|
for _, f := range matches {
|
||||||
|
require.NoError(t, os.Remove(f), "removing pcapng file %s", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketConnInitialize_CreatesFile(t *testing.T) {
|
||||||
|
client, server := startUDPPair(t)
|
||||||
|
defer server.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &PacketConn{Prefix: prefix, PacketConn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapngPacket(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketConnWriteTo(t *testing.T) {
|
||||||
|
client, server := startUDPPair(t)
|
||||||
|
defer server.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &PacketConn{Prefix: prefix, PacketConn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapngPacket(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
n, err := c.WriteTo([]byte("hello world"), server.LocalAddr())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 11, n)
|
||||||
|
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
server.SetReadDeadline(time.Now().Add(2 * time.Second)) //nolint:errcheck
|
||||||
|
rn, _, err := server.ReadFromUDP(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("hello world"), buf[:rn])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketConnReadFrom(t *testing.T) {
|
||||||
|
client, server := startUDPPair(t)
|
||||||
|
defer server.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &PacketConn{Prefix: prefix, PacketConn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapngPacket(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
_, err := server.WriteTo([]byte("incoming data"), client.LocalAddr())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
c.SetReadDeadline(time.Now().Add(2 * time.Second)) //nolint:errcheck
|
||||||
|
n, addr, err := c.ReadFrom(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("incoming data"), buf[:n])
|
||||||
|
require.NotNil(t, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketConnMultipleWriteRead(t *testing.T) {
|
||||||
|
client, server := startUDPPair(t)
|
||||||
|
defer server.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &PacketConn{Prefix: prefix, PacketConn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapngPacket(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
serverAddr := server.LocalAddr()
|
||||||
|
for _, msg := range []string{"foo", "bar", "baz"} {
|
||||||
|
n, err := c.WriteTo([]byte(msg), serverAddr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, len(msg), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
server.SetReadDeadline(time.Now().Add(2 * time.Second)) //nolint:errcheck
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
received := make([]byte, 0, 9)
|
||||||
|
for range 3 {
|
||||||
|
n, _, err := server.ReadFromUDP(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
received = append(received, buf[:n]...)
|
||||||
|
}
|
||||||
|
require.Equal(t, []byte("foobarbaz"), received)
|
||||||
|
|
||||||
|
for _, msg := range []string{"abcde", "fghij"} {
|
||||||
|
_, err := server.WriteTo([]byte(msg), client.LocalAddr())
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetReadDeadline(time.Now().Add(2 * time.Second)) //nolint:errcheck
|
||||||
|
readReceived := make([]byte, 0, 10)
|
||||||
|
for range 2 {
|
||||||
|
n, _, err := c.ReadFrom(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
readReceived = append(readReceived, buf[:n]...)
|
||||||
|
}
|
||||||
|
require.Equal(t, []byte("abcdefghij"), readReceived)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketConnCloseIdempotent(t *testing.T) {
|
||||||
|
client, server := startUDPPair(t)
|
||||||
|
defer server.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &PacketConn{Prefix: prefix, PacketConn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapngPacket(t, prefix)
|
||||||
|
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketConnDelegatesAddrMethods(t *testing.T) {
|
||||||
|
client, server := startUDPPair(t)
|
||||||
|
defer server.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &PacketConn{Prefix: prefix, PacketConn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapngPacket(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
require.Equal(t, client.LocalAddr(), c.LocalAddr())
|
||||||
|
|
||||||
|
require.NoError(t, c.SetDeadline(time.Now().Add(time.Second)))
|
||||||
|
require.NoError(t, c.SetReadDeadline(time.Now().Add(time.Second)))
|
||||||
|
require.NoError(t, c.SetWriteDeadline(time.Now().Add(time.Second)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketConnReadFromRecordsSource(t *testing.T) {
|
||||||
|
client, server := startUDPPair(t)
|
||||||
|
defer server.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
prefix := filepath.Join(t.TempDir(), "capture")
|
||||||
|
c := &PacketConn{Prefix: prefix, PacketConn: client}
|
||||||
|
require.NoError(t, c.Initialize())
|
||||||
|
|
||||||
|
defer cleanupPcapngPacket(t, prefix)
|
||||||
|
defer c.Close() //nolint:errcheck
|
||||||
|
|
||||||
|
_, err := server.WriteTo([]byte("ping"), client.LocalAddr())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
c.SetReadDeadline(time.Now().Add(2 * time.Second)) //nolint:errcheck
|
||||||
|
n, addr, err := c.ReadFrom(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("ping"), buf[:n])
|
||||||
|
|
||||||
|
// The reported source address should match the server's address.
|
||||||
|
require.Equal(t, server.LocalAddr().String(), addr.String())
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ type serverAuthManager interface {
|
|||||||
// Server is the playback server.
|
// Server is the playback server.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Address string
|
Address string
|
||||||
|
DumpPackets bool
|
||||||
Encryption bool
|
Encryption bool
|
||||||
ServerKey string
|
ServerKey string
|
||||||
ServerCert string
|
ServerCert string
|
||||||
@@ -48,15 +49,17 @@ func (s *Server) Initialize() error {
|
|||||||
router.GET("/get", s.onGet)
|
router.GET("/get", s.onGet)
|
||||||
|
|
||||||
s.httpServer = &httpp.Server{
|
s.httpServer = &httpp.Server{
|
||||||
Address: s.Address,
|
Address: s.Address,
|
||||||
AllowOrigins: s.AllowOrigins,
|
AllowOrigins: s.AllowOrigins,
|
||||||
ReadTimeout: time.Duration(s.ReadTimeout),
|
DumpPackets: s.DumpPackets,
|
||||||
WriteTimeout: time.Duration(s.WriteTimeout),
|
DumpPacketsPrefix: "playback_server_conn",
|
||||||
Encryption: s.Encryption,
|
ReadTimeout: time.Duration(s.ReadTimeout),
|
||||||
ServerCert: s.ServerCert,
|
WriteTimeout: time.Duration(s.WriteTimeout),
|
||||||
ServerKey: s.ServerKey,
|
Encryption: s.Encryption,
|
||||||
Handler: router,
|
ServerCert: s.ServerCert,
|
||||||
Parent: s,
|
ServerKey: s.ServerKey,
|
||||||
|
Handler: router,
|
||||||
|
Parent: s,
|
||||||
}
|
}
|
||||||
err := s.httpServer.Initialize()
|
err := s.httpServer.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+12
-9
@@ -27,6 +27,7 @@ type pprofParent interface {
|
|||||||
// PPROF is a pprof exporter.
|
// PPROF is a pprof exporter.
|
||||||
type PPROF struct {
|
type PPROF struct {
|
||||||
Address string
|
Address string
|
||||||
|
DumpPackets bool
|
||||||
Encryption bool
|
Encryption bool
|
||||||
ServerKey string
|
ServerKey string
|
||||||
ServerCert string
|
ServerCert string
|
||||||
@@ -51,15 +52,17 @@ func (pp *PPROF) Initialize() error {
|
|||||||
pprof.Register(router)
|
pprof.Register(router)
|
||||||
|
|
||||||
pp.httpServer = &httpp.Server{
|
pp.httpServer = &httpp.Server{
|
||||||
Address: pp.Address,
|
Address: pp.Address,
|
||||||
AllowOrigins: pp.AllowOrigins,
|
DumpPackets: pp.DumpPackets,
|
||||||
ReadTimeout: time.Duration(pp.ReadTimeout),
|
AllowOrigins: pp.AllowOrigins,
|
||||||
WriteTimeout: time.Duration(pp.WriteTimeout),
|
DumpPacketsPrefix: "pprof_server_conn",
|
||||||
Encryption: pp.Encryption,
|
ReadTimeout: time.Duration(pp.ReadTimeout),
|
||||||
ServerCert: pp.ServerCert,
|
WriteTimeout: time.Duration(pp.WriteTimeout),
|
||||||
ServerKey: pp.ServerKey,
|
Encryption: pp.Encryption,
|
||||||
Handler: router,
|
ServerCert: pp.ServerCert,
|
||||||
Parent: pp,
|
ServerKey: pp.ServerKey,
|
||||||
|
Handler: router,
|
||||||
|
Parent: pp,
|
||||||
}
|
}
|
||||||
err := pp.httpServer.Initialize()
|
err := pp.httpServer.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/bluenviron/mediamtx/internal/certloader"
|
"github.com/bluenviron/mediamtx/internal/certloader"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/packetdumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/restrictnetwork"
|
"github.com/bluenviron/mediamtx/internal/restrictnetwork"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,15 +31,17 @@ func (nilWriter) Write(p []byte) (int, error) {
|
|||||||
// - server header
|
// - server header
|
||||||
// - filtering of invalid requests
|
// - filtering of invalid requests
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Address string
|
Address string
|
||||||
AllowOrigins []string
|
AllowOrigins []string
|
||||||
ReadTimeout time.Duration
|
DumpPackets bool
|
||||||
WriteTimeout time.Duration
|
DumpPacketsPrefix string
|
||||||
Encryption bool
|
ReadTimeout time.Duration
|
||||||
ServerCert string
|
WriteTimeout time.Duration
|
||||||
ServerKey string
|
Encryption bool
|
||||||
Handler http.Handler
|
ServerCert string
|
||||||
Parent logger.Writer
|
ServerKey string
|
||||||
|
Handler http.Handler
|
||||||
|
Parent logger.Writer
|
||||||
|
|
||||||
ln net.Listener
|
ln net.Listener
|
||||||
inner *http.Server
|
inner *http.Server
|
||||||
@@ -96,6 +99,13 @@ func (s *Server) Initialize() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.DumpPackets {
|
||||||
|
s.ln = &packetdumper.Listener{
|
||||||
|
Prefix: s.DumpPacketsPrefix,
|
||||||
|
Listener: s.ln,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if network == "unix" {
|
if network == "unix" {
|
||||||
os.Chmod(address, 0o755) //nolint:errcheck
|
os.Chmod(address, 0o755) //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,161 +0,0 @@
|
|||||||
// Package udp contains utilities to work with the UDP protocol.
|
|
||||||
package udp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v5/pkg/multicast"
|
|
||||||
"github.com/bluenviron/gortsplib/v5/pkg/readbuffer"
|
|
||||||
"github.com/bluenviron/mediamtx/internal/restrictnetwork"
|
|
||||||
)
|
|
||||||
|
|
||||||
type packetConn interface {
|
|
||||||
net.PacketConn
|
|
||||||
SetReadBuffer(bytes int) error
|
|
||||||
SyscallConn() (syscall.RawConn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type udpConn struct {
|
|
||||||
pc net.PacketConn
|
|
||||||
sourceIP net.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *udpConn) Close() error {
|
|
||||||
return r.pc.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *udpConn) Read(p []byte) (int, error) {
|
|
||||||
for {
|
|
||||||
n, addr, err := r.pc.ReadFrom(p)
|
|
||||||
|
|
||||||
if r.sourceIP != nil && addr != nil && !addr.(*net.UDPAddr).IP.Equal(r.sourceIP) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *udpConn) Write(_ []byte) (int, error) {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *udpConn) LocalAddr() net.Addr {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *udpConn) RemoteAddr() net.Addr {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *udpConn) SetDeadline(_ time.Time) error {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *udpConn) SetReadDeadline(t time.Time) error {
|
|
||||||
return r.pc.SetReadDeadline(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *udpConn) SetWriteDeadline(_ time.Time) error {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultInterfaceForMulticast(multicastAddr *net.UDPAddr) (*net.Interface, error) {
|
|
||||||
conn, err := net.Dial("udp4", multicastAddr.String())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
|
||||||
conn.Close()
|
|
||||||
|
|
||||||
interfaces, err := net.Interfaces()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, iface := range interfaces {
|
|
||||||
var addrs []net.Addr
|
|
||||||
addrs, err = iface.Addrs()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, addr := range addrs {
|
|
||||||
var ip net.IP
|
|
||||||
switch v := addr.(type) {
|
|
||||||
case *net.IPNet:
|
|
||||||
ip = v.IP
|
|
||||||
case *net.IPAddr:
|
|
||||||
ip = v.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip != nil && ip.Equal(localAddr.IP) {
|
|
||||||
return &iface, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("could not find any interface for using multicast address %s", multicastAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen creates a UDP listener on the given URL.
|
|
||||||
func Listen(u *url.URL, udpReadBufferSize int) (net.Conn, error) {
|
|
||||||
q := u.Query()
|
|
||||||
var sourceIP net.IP
|
|
||||||
|
|
||||||
if src := q.Get("source"); src != "" {
|
|
||||||
sourceIP = net.ParseIP(src)
|
|
||||||
if sourceIP == nil {
|
|
||||||
return nil, fmt.Errorf("invalid source IP")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addr, err := net.ResolveUDPAddr("udp", u.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pc packetConn
|
|
||||||
|
|
||||||
if ip4 := addr.IP.To4(); ip4 != nil && addr.IP.IsMulticast() {
|
|
||||||
var intf *net.Interface
|
|
||||||
|
|
||||||
if intfName := q.Get("interface"); intfName != "" {
|
|
||||||
intf, err = net.InterfaceByName(intfName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
intf, err = defaultInterfaceForMulticast(addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pc, err = multicast.NewSingleConn(intf, addr.String(), net.ListenPacket)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var tmp net.PacketConn
|
|
||||||
tmp, err = net.ListenPacket(restrictnetwork.Restrict("udp", addr.String()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pc = tmp.(*net.UDPConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
if udpReadBufferSize != 0 {
|
|
||||||
err = readbuffer.SetReadBuffer(pc, udpReadBufferSize)
|
|
||||||
if err != nil {
|
|
||||||
pc.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &udpConn{pc: pc, sourceIP: sourceIP}, nil
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
// Package udp contains utilities to work with the UDP protocol.
|
||||||
|
package udp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bluenviron/gortsplib/v5/pkg/multicast"
|
||||||
|
"github.com/bluenviron/gortsplib/v5/pkg/readbuffer"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/restrictnetwork"
|
||||||
|
)
|
||||||
|
|
||||||
|
type packetConn interface {
|
||||||
|
net.PacketConn
|
||||||
|
SetReadBuffer(bytes int) error
|
||||||
|
SyscallConn() (syscall.RawConn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultInterfaceForMulticast(multicastAddr *net.UDPAddr) (*net.Interface, error) {
|
||||||
|
conn, err := net.Dial("udp4", multicastAddr.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||||
|
conn.Close()
|
||||||
|
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
var addrs []net.Addr
|
||||||
|
addrs, err = iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
var ip net.IP
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = v.IP
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = v.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip != nil && ip.Equal(localAddr.IP) {
|
||||||
|
return &iface, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("could not find any interface for using multicast address %s", multicastAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listener is a listener on a UDP socket.
|
||||||
|
type Listener struct {
|
||||||
|
Address string
|
||||||
|
Source string
|
||||||
|
IntfName string
|
||||||
|
UDPReadBufferSize int
|
||||||
|
ListenPacket func(network, address string) (net.PacketConn, error)
|
||||||
|
|
||||||
|
pc packetConn
|
||||||
|
sourceIP net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes the listener.
|
||||||
|
func (l *Listener) Initialize() error {
|
||||||
|
if l.ListenPacket == nil {
|
||||||
|
l.ListenPacket = net.ListenPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Source != "" {
|
||||||
|
l.sourceIP = net.ParseIP(l.Source)
|
||||||
|
if l.sourceIP == nil {
|
||||||
|
return fmt.Errorf("invalid source IP")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", l.Address)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip4 := addr.IP.To4(); ip4 != nil && addr.IP.IsMulticast() {
|
||||||
|
var intf *net.Interface
|
||||||
|
|
||||||
|
if l.IntfName != "" {
|
||||||
|
intf, err = net.InterfaceByName(l.IntfName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
intf, err = defaultInterfaceForMulticast(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.pc, err = multicast.NewSingleConn(intf, addr.String(), l.ListenPacket)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var tmp net.PacketConn
|
||||||
|
tmp, err = l.ListenPacket(restrictnetwork.Restrict("udp", addr.String()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l.pc = tmp.(packetConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.UDPReadBufferSize != 0 {
|
||||||
|
err = readbuffer.SetReadBuffer(l.pc, l.UDPReadBufferSize)
|
||||||
|
if err != nil {
|
||||||
|
l.pc.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the listener.
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
return l.pc.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements net.Conn.
|
||||||
|
func (l *Listener) Read(p []byte) (int, error) {
|
||||||
|
for {
|
||||||
|
n, addr, err := l.pc.ReadFrom(p)
|
||||||
|
|
||||||
|
if l.sourceIP != nil && addr != nil && !addr.(*net.UDPAddr).IP.Equal(l.sourceIP) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implements net.Conn.
|
||||||
|
func (l *Listener) Write(_ []byte) (int, error) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr implements net.Conn.
|
||||||
|
func (l *Listener) LocalAddr() net.Addr {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteAddr implements net.Conn.
|
||||||
|
func (l *Listener) RemoteAddr() net.Addr {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeadline implements net.Conn.
|
||||||
|
func (l *Listener) SetDeadline(_ time.Time) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline implements net.Conn.
|
||||||
|
func (l *Listener) SetReadDeadline(t time.Time) error {
|
||||||
|
return l.pc.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline implements net.Conn.
|
||||||
|
func (l *Listener) SetWriteDeadline(_ time.Time) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package udp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -10,26 +9,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestListen(t *testing.T) {
|
func TestListen(t *testing.T) {
|
||||||
u, err := url.Parse("udp://127.0.0.1:0")
|
l := &Listener{
|
||||||
|
Address: "127.0.0.1:0",
|
||||||
|
UDPReadBufferSize: 4096,
|
||||||
|
}
|
||||||
|
err := l.Initialize()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
defer l.Close() //nolint:errcheck
|
||||||
conn, err := Listen(u, 4096)
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
|
||||||
buf := make([]byte, 1024)
|
err2 := l.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||||
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
|
||||||
n, err2 := conn.Read(buf)
|
|
||||||
require.NoError(t, err2)
|
require.NoError(t, err2)
|
||||||
|
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, err2 := l.Read(buf)
|
||||||
|
require.NoError(t, err2)
|
||||||
|
|
||||||
require.Equal(t, []byte("testing"), buf[:n])
|
require.Equal(t, []byte("testing"), buf[:n])
|
||||||
}()
|
}()
|
||||||
|
|
||||||
localAddr := conn.(*udpConn).pc.LocalAddr().(*net.UDPAddr)
|
localAddr := l.pc.LocalAddr().(*net.UDPAddr)
|
||||||
|
|
||||||
clientConn, err := net.DialUDP("udp", nil, localAddr)
|
clientConn, err := net.DialUDP("udp", nil, localAddr)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package udp
|
||||||
|
|
||||||
|
import "net/url"
|
||||||
|
|
||||||
|
// Params are the parameters of a UDP listener.
|
||||||
|
type Params struct {
|
||||||
|
Address string
|
||||||
|
Source string
|
||||||
|
IntfName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLToParams converts a URL to Params.
|
||||||
|
func URLToParams(u *url.URL) *Params {
|
||||||
|
return &Params{
|
||||||
|
Address: u.Host,
|
||||||
|
Source: u.Query().Get("source"),
|
||||||
|
IntfName: u.Query().Get("interface"),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
// Package unix contains utilities to work with Unix sockets.
|
|
||||||
package unix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type unixConn struct {
|
|
||||||
l net.Listener
|
|
||||||
c net.Conn
|
|
||||||
mutex sync.Mutex
|
|
||||||
closed bool
|
|
||||||
deadline time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) Close() error {
|
|
||||||
r.mutex.Lock()
|
|
||||||
defer r.mutex.Unlock()
|
|
||||||
|
|
||||||
r.closed = true
|
|
||||||
|
|
||||||
r.l.Close()
|
|
||||||
|
|
||||||
if r.c != nil {
|
|
||||||
r.c.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) acceptWithDeadline() (net.Conn, error) {
|
|
||||||
done := make(chan struct{})
|
|
||||||
defer func() { <-done }()
|
|
||||||
|
|
||||||
terminate := make(chan struct{})
|
|
||||||
defer close(terminate)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer close(done)
|
|
||||||
select {
|
|
||||||
case <-time.After(time.Until(r.deadline)):
|
|
||||||
r.l.Close()
|
|
||||||
case <-terminate:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
c, err := r.l.Accept()
|
|
||||||
if err != nil {
|
|
||||||
if time.Now().After(r.deadline) {
|
|
||||||
return nil, fmt.Errorf("deadline exceeded")
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) setConn(c net.Conn) error {
|
|
||||||
r.mutex.Lock()
|
|
||||||
defer r.mutex.Unlock()
|
|
||||||
|
|
||||||
if r.closed {
|
|
||||||
return fmt.Errorf("closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.c = c
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) Read(p []byte) (int, error) {
|
|
||||||
if r.c == nil {
|
|
||||||
c, err := r.acceptWithDeadline()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.setConn(c)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r.c.SetReadDeadline(r.deadline)
|
|
||||||
return r.c.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) Write(_ []byte) (int, error) {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) LocalAddr() net.Addr {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) RemoteAddr() net.Addr {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) SetDeadline(_ time.Time) error {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) SetReadDeadline(t time.Time) error {
|
|
||||||
r.deadline = t
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *unixConn) SetWriteDeadline(_ time.Time) error {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen creates a Unix listener on the given URL.
|
|
||||||
func Listen(u *url.URL) (net.Conn, error) {
|
|
||||||
var pa string
|
|
||||||
if u.Path != "" {
|
|
||||||
pa = u.Path
|
|
||||||
} else {
|
|
||||||
pa = u.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
if pa == "" {
|
|
||||||
return nil, fmt.Errorf("invalid unix path")
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Remove(pa)
|
|
||||||
|
|
||||||
socket, err := net.Listen("unix", pa)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &unixConn{l: socket}, nil
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
// Package unix contains utilities to work with Unix sockets.
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listener is a listener on a Unix socket.
|
||||||
|
type Listener struct {
|
||||||
|
Path string
|
||||||
|
|
||||||
|
l net.Listener
|
||||||
|
c net.Conn
|
||||||
|
mutex sync.Mutex
|
||||||
|
closed bool
|
||||||
|
deadline time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes the listener.
|
||||||
|
func (l *Listener) Initialize() error {
|
||||||
|
if l.Path == "" {
|
||||||
|
return fmt.Errorf("invalid unix path")
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Remove(l.Path)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
l.l, err = net.Listen("unix", l.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the listener.
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
l.mutex.Lock()
|
||||||
|
defer l.mutex.Unlock()
|
||||||
|
|
||||||
|
l.closed = true
|
||||||
|
|
||||||
|
l.l.Close()
|
||||||
|
|
||||||
|
if l.c != nil {
|
||||||
|
l.c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) acceptWithDeadline() (net.Conn, error) {
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer func() { <-done }()
|
||||||
|
|
||||||
|
terminate := make(chan struct{})
|
||||||
|
defer close(terminate)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Until(l.deadline)):
|
||||||
|
l.l.Close()
|
||||||
|
case <-terminate:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c, err := l.l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
if time.Now().After(l.deadline) {
|
||||||
|
return nil, fmt.Errorf("deadline exceeded")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) setConn(c net.Conn) error {
|
||||||
|
l.mutex.Lock()
|
||||||
|
defer l.mutex.Unlock()
|
||||||
|
|
||||||
|
if l.closed {
|
||||||
|
return fmt.Errorf("closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
l.c = c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Read(p []byte) (int, error) {
|
||||||
|
if l.c == nil {
|
||||||
|
c, err := l.acceptWithDeadline()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.setConn(c)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.c.SetReadDeadline(l.deadline)
|
||||||
|
return l.c.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implements net.Conn.
|
||||||
|
func (l *Listener) Write(_ []byte) (int, error) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr implements net.Conn.
|
||||||
|
func (l *Listener) LocalAddr() net.Addr {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteAddr implements net.Conn.
|
||||||
|
func (l *Listener) RemoteAddr() net.Addr {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeadline implements net.Conn.
|
||||||
|
func (l *Listener) SetDeadline(_ time.Time) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline implements net.Conn.
|
||||||
|
func (l *Listener) SetReadDeadline(t time.Time) error {
|
||||||
|
l.deadline = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline implements net.Conn.
|
||||||
|
func (l *Listener) SetWriteDeadline(_ time.Time) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package unix
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -16,12 +15,12 @@ func TestListen(t *testing.T) {
|
|||||||
socket.Close()
|
socket.Close()
|
||||||
defer os.Remove(socket.Name())
|
defer os.Remove(socket.Name())
|
||||||
|
|
||||||
u, err := url.Parse("unix://" + socket.Name())
|
l := &Listener{
|
||||||
|
Path: socket.Name(),
|
||||||
|
}
|
||||||
|
err = l.Initialize()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
defer l.Close() //nolint:errcheck
|
||||||
conn, err := Listen(u)
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
@@ -29,8 +28,8 @@ func TestListen(t *testing.T) {
|
|||||||
defer close(done)
|
defer close(done)
|
||||||
|
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
l.SetReadDeadline(time.Now().Add(2 * time.Second)) //nolint:errcheck
|
||||||
n, err2 := conn.Read(buf)
|
n, err2 := l.Read(buf)
|
||||||
require.NoError(t, err2)
|
require.NoError(t, err2)
|
||||||
require.Equal(t, []byte("testing"), buf[:n])
|
require.Equal(t, []byte("testing"), buf[:n])
|
||||||
}()
|
}()
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package unix
|
||||||
|
|
||||||
|
import "net/url"
|
||||||
|
|
||||||
|
// Params are the parameters of a unix listener.
|
||||||
|
type Params struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLToParams converts a URL to Params.
|
||||||
|
func URLToParams(u *url.URL) *Params {
|
||||||
|
var pa string
|
||||||
|
if u.Path != "" {
|
||||||
|
pa = u.Path
|
||||||
|
} else {
|
||||||
|
pa = u.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Params{
|
||||||
|
Path: pa,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ func mergePathAndQuery(path string, rawQuery string) string {
|
|||||||
|
|
||||||
type httpServer struct {
|
type httpServer struct {
|
||||||
address string
|
address string
|
||||||
|
dumpPackets bool
|
||||||
encryption bool
|
encryption bool
|
||||||
serverKey string
|
serverKey string
|
||||||
serverCert string
|
serverCert string
|
||||||
@@ -58,15 +59,17 @@ func (s *httpServer) initialize() error {
|
|||||||
router.Use(s.onRequest)
|
router.Use(s.onRequest)
|
||||||
|
|
||||||
s.inner = &httpp.Server{
|
s.inner = &httpp.Server{
|
||||||
Address: s.address,
|
Address: s.address,
|
||||||
AllowOrigins: s.allowOrigins,
|
AllowOrigins: s.allowOrigins,
|
||||||
ReadTimeout: time.Duration(s.readTimeout),
|
DumpPackets: s.dumpPackets,
|
||||||
WriteTimeout: time.Duration(s.writeTimeout),
|
DumpPacketsPrefix: "hls_server_conn",
|
||||||
Encryption: s.encryption,
|
ReadTimeout: time.Duration(s.readTimeout),
|
||||||
ServerCert: s.serverCert,
|
WriteTimeout: time.Duration(s.writeTimeout),
|
||||||
ServerKey: s.serverKey,
|
Encryption: s.encryption,
|
||||||
Handler: router,
|
ServerCert: s.serverCert,
|
||||||
Parent: s,
|
ServerKey: s.serverKey,
|
||||||
|
Handler: router,
|
||||||
|
Parent: s,
|
||||||
}
|
}
|
||||||
err := s.inner.Initialize()
|
err := s.inner.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ type serverParent interface {
|
|||||||
// Server is a HLS server.
|
// Server is a HLS server.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Address string
|
Address string
|
||||||
|
DumpPackets bool
|
||||||
Encryption bool
|
Encryption bool
|
||||||
ServerKey string
|
ServerKey string
|
||||||
ServerCert string
|
ServerCert string
|
||||||
@@ -121,6 +122,7 @@ func (s *Server) Initialize() error {
|
|||||||
|
|
||||||
s.httpServer = &httpServer{
|
s.httpServer = &httpServer{
|
||||||
address: s.Address,
|
address: s.Address,
|
||||||
|
dumpPackets: s.DumpPackets,
|
||||||
encryption: s.Encryption,
|
encryption: s.Encryption,
|
||||||
serverKey: s.ServerKey,
|
serverKey: s.ServerKey,
|
||||||
serverCert: s.ServerCert,
|
serverCert: s.ServerCert,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/bluenviron/mediamtx/internal/defs"
|
"github.com/bluenviron/mediamtx/internal/defs"
|
||||||
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/packetdumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/restrictnetwork"
|
"github.com/bluenviron/mediamtx/internal/restrictnetwork"
|
||||||
"github.com/bluenviron/mediamtx/internal/stream"
|
"github.com/bluenviron/mediamtx/internal/stream"
|
||||||
)
|
)
|
||||||
@@ -74,6 +75,7 @@ type serverParent interface {
|
|||||||
// Server is a RTMP server.
|
// Server is a RTMP server.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Address string
|
Address string
|
||||||
|
DumpPackets bool
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
WriteTimeout conf.Duration
|
WriteTimeout conf.Duration
|
||||||
IsTLS bool
|
IsTLS bool
|
||||||
@@ -106,31 +108,36 @@ type Server struct {
|
|||||||
|
|
||||||
// Initialize initializes the server.
|
// Initialize initializes the server.
|
||||||
func (s *Server) Initialize() error {
|
func (s *Server) Initialize() error {
|
||||||
ln, err := func() (net.Listener, error) {
|
var err error
|
||||||
if !s.IsTLS {
|
s.ln, err = net.Listen(restrictnetwork.Restrict("tcp", s.Address))
|
||||||
return net.Listen(restrictnetwork.Restrict("tcp", s.Address))
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.DumpPackets {
|
||||||
|
s.ln = &packetdumper.Listener{
|
||||||
|
Prefix: "rtmp_server_conn",
|
||||||
|
Listener: s.ln,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.IsTLS {
|
||||||
s.loader = &certloader.CertLoader{
|
s.loader = &certloader.CertLoader{
|
||||||
CertPath: s.ServerCert,
|
CertPath: s.ServerCert,
|
||||||
KeyPath: s.ServerKey,
|
KeyPath: s.ServerKey,
|
||||||
Parent: s.Parent,
|
Parent: s.Parent,
|
||||||
}
|
}
|
||||||
err := s.loader.Initialize()
|
err = s.loader.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
s.ln.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
network, address := restrictnetwork.Restrict("tcp", s.Address)
|
s.ln = tls.NewListener(s.ln, &tls.Config{GetCertificate: s.loader.GetCertificate()})
|
||||||
return tls.Listen(network, address, &tls.Config{GetCertificate: s.loader.GetCertificate()})
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
|
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
|
||||||
|
|
||||||
s.ln = ln
|
|
||||||
s.conns = make(map[*conn]struct{})
|
s.conns = make(map[*conn]struct{})
|
||||||
s.chNewConn = make(chan net.Conn)
|
s.chNewConn = make(chan net.Conn)
|
||||||
s.chAcceptErr = make(chan error)
|
s.chAcceptErr = make(chan error)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -23,6 +24,7 @@ import (
|
|||||||
"github.com/bluenviron/mediamtx/internal/defs"
|
"github.com/bluenviron/mediamtx/internal/defs"
|
||||||
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/packetdumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/stream"
|
"github.com/bluenviron/mediamtx/internal/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,6 +92,7 @@ type serverParent interface {
|
|||||||
type Server struct {
|
type Server struct {
|
||||||
Address string
|
Address string
|
||||||
AuthMethods []auth.VerifyMethod
|
AuthMethods []auth.VerifyMethod
|
||||||
|
DumpPackets bool
|
||||||
UDPReadBufferSize uint
|
UDPReadBufferSize uint
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
WriteTimeout conf.Duration
|
WriteTimeout conf.Duration
|
||||||
@@ -165,6 +168,18 @@ func (s *Server) Initialize() error {
|
|||||||
s.srv.TLSConfig = &tls.Config{GetCertificate: s.loader.GetCertificate()}
|
s.srv.TLSConfig = &tls.Config{GetCertificate: s.loader.GetCertificate()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.DumpPackets {
|
||||||
|
s.srv.Listen = (&packetdumper.Listen{
|
||||||
|
Prefix: "rtsp_server_conn",
|
||||||
|
Listen: net.Listen,
|
||||||
|
}).Do
|
||||||
|
|
||||||
|
s.srv.ListenPacket = (&packetdumper.ListenPacket{
|
||||||
|
Prefix: "rtsp_server_packetconn",
|
||||||
|
ListenPacket: net.ListenPacket,
|
||||||
|
}).Do
|
||||||
|
}
|
||||||
|
|
||||||
err := s.srv.Start()
|
err := s.srv.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ func sessionLocation(publish bool, path string, rawQuery string, secret uuid.UUI
|
|||||||
|
|
||||||
type httpServer struct {
|
type httpServer struct {
|
||||||
address string
|
address string
|
||||||
|
dumpPackets bool
|
||||||
encryption bool
|
encryption bool
|
||||||
serverKey string
|
serverKey string
|
||||||
serverCert string
|
serverCert string
|
||||||
@@ -96,15 +97,17 @@ func (s *httpServer) initialize() error {
|
|||||||
router.Use(s.onRequest)
|
router.Use(s.onRequest)
|
||||||
|
|
||||||
s.inner = &httpp.Server{
|
s.inner = &httpp.Server{
|
||||||
Address: s.address,
|
Address: s.address,
|
||||||
AllowOrigins: s.allowOrigins,
|
AllowOrigins: s.allowOrigins,
|
||||||
ReadTimeout: time.Duration(s.readTimeout),
|
DumpPackets: s.dumpPackets,
|
||||||
WriteTimeout: time.Duration(s.writeTimeout),
|
DumpPacketsPrefix: "webrtc_server_conn",
|
||||||
Encryption: s.encryption,
|
ReadTimeout: time.Duration(s.readTimeout),
|
||||||
ServerCert: s.serverCert,
|
WriteTimeout: time.Duration(s.writeTimeout),
|
||||||
ServerKey: s.serverKey,
|
Encryption: s.encryption,
|
||||||
Handler: router,
|
ServerCert: s.serverCert,
|
||||||
Parent: s,
|
ServerKey: s.serverKey,
|
||||||
|
Handler: router,
|
||||||
|
Parent: s,
|
||||||
}
|
}
|
||||||
err := s.inner.Initialize()
|
err := s.inner.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ type serverParent interface {
|
|||||||
// Server is a WebRTC server.
|
// Server is a WebRTC server.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Address string
|
Address string
|
||||||
|
DumpPackets bool
|
||||||
Encryption bool
|
Encryption bool
|
||||||
ServerKey string
|
ServerKey string
|
||||||
ServerCert string
|
ServerCert string
|
||||||
@@ -251,6 +252,7 @@ func (s *Server) Initialize() error {
|
|||||||
|
|
||||||
s.httpServer = &httpServer{
|
s.httpServer = &httpServer{
|
||||||
address: s.Address,
|
address: s.Address,
|
||||||
|
dumpPackets: s.DumpPackets,
|
||||||
encryption: s.Encryption,
|
encryption: s.Encryption,
|
||||||
serverKey: s.ServerKey,
|
serverKey: s.ServerKey,
|
||||||
serverCert: s.ServerCert,
|
serverCert: s.ServerCert,
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ type handlerParent interface {
|
|||||||
type Handler struct {
|
type Handler struct {
|
||||||
Conf *conf.Path
|
Conf *conf.Path
|
||||||
LogLevel conf.LogLevel
|
LogLevel conf.LogLevel
|
||||||
|
DumpPackets bool
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
WriteTimeout conf.Duration
|
WriteTimeout conf.Duration
|
||||||
WriteQueueSize int
|
WriteQueueSize int
|
||||||
@@ -102,6 +103,7 @@ func (s *Handler) Initialize() {
|
|||||||
strings.HasPrefix(s.Conf.Source, "rtsp+ws://") ||
|
strings.HasPrefix(s.Conf.Source, "rtsp+ws://") ||
|
||||||
strings.HasPrefix(s.Conf.Source, "rtsps+ws://"):
|
strings.HasPrefix(s.Conf.Source, "rtsps+ws://"):
|
||||||
s.instance = &ssrtsp.Source{
|
s.instance = &ssrtsp.Source{
|
||||||
|
DumpPackets: s.DumpPackets,
|
||||||
ReadTimeout: s.ReadTimeout,
|
ReadTimeout: s.ReadTimeout,
|
||||||
WriteTimeout: s.WriteTimeout,
|
WriteTimeout: s.WriteTimeout,
|
||||||
WriteQueueSize: s.WriteQueueSize,
|
WriteQueueSize: s.WriteQueueSize,
|
||||||
@@ -112,6 +114,7 @@ func (s *Handler) Initialize() {
|
|||||||
case strings.HasPrefix(s.Conf.Source, "rtmp://") ||
|
case strings.HasPrefix(s.Conf.Source, "rtmp://") ||
|
||||||
strings.HasPrefix(s.Conf.Source, "rtmps://"):
|
strings.HasPrefix(s.Conf.Source, "rtmps://"):
|
||||||
s.instance = &ssrtmp.Source{
|
s.instance = &ssrtmp.Source{
|
||||||
|
DumpPackets: s.DumpPackets,
|
||||||
ReadTimeout: s.ReadTimeout,
|
ReadTimeout: s.ReadTimeout,
|
||||||
WriteTimeout: s.WriteTimeout,
|
WriteTimeout: s.WriteTimeout,
|
||||||
Parent: s,
|
Parent: s,
|
||||||
@@ -120,6 +123,7 @@ func (s *Handler) Initialize() {
|
|||||||
case strings.HasPrefix(s.Conf.Source, "http://") ||
|
case strings.HasPrefix(s.Conf.Source, "http://") ||
|
||||||
strings.HasPrefix(s.Conf.Source, "https://"):
|
strings.HasPrefix(s.Conf.Source, "https://"):
|
||||||
s.instance = &sshls.Source{
|
s.instance = &sshls.Source{
|
||||||
|
DumpPackets: s.DumpPackets,
|
||||||
ReadTimeout: s.ReadTimeout,
|
ReadTimeout: s.ReadTimeout,
|
||||||
Parent: s,
|
Parent: s,
|
||||||
}
|
}
|
||||||
@@ -128,6 +132,7 @@ func (s *Handler) Initialize() {
|
|||||||
strings.HasPrefix(s.Conf.Source, "udp+mpegts://") ||
|
strings.HasPrefix(s.Conf.Source, "udp+mpegts://") ||
|
||||||
strings.HasPrefix(s.Conf.Source, "unix+mpegts://"):
|
strings.HasPrefix(s.Conf.Source, "unix+mpegts://"):
|
||||||
s.instance = &ssmpegts.Source{
|
s.instance = &ssmpegts.Source{
|
||||||
|
DumpPackets: s.DumpPackets,
|
||||||
ReadTimeout: s.ReadTimeout,
|
ReadTimeout: s.ReadTimeout,
|
||||||
UDPReadBufferSize: s.UDPReadBufferSize,
|
UDPReadBufferSize: s.UDPReadBufferSize,
|
||||||
Parent: s,
|
Parent: s,
|
||||||
@@ -142,6 +147,7 @@ func (s *Handler) Initialize() {
|
|||||||
case strings.HasPrefix(s.Conf.Source, "whep://") ||
|
case strings.HasPrefix(s.Conf.Source, "whep://") ||
|
||||||
strings.HasPrefix(s.Conf.Source, "wheps://"):
|
strings.HasPrefix(s.Conf.Source, "wheps://"):
|
||||||
s.instance = &sswebrtc.Source{
|
s.instance = &sswebrtc.Source{
|
||||||
|
DumpPackets: s.DumpPackets,
|
||||||
ReadTimeout: s.ReadTimeout,
|
ReadTimeout: s.ReadTimeout,
|
||||||
UDPReadBufferSize: s.UDPReadBufferSize,
|
UDPReadBufferSize: s.UDPReadBufferSize,
|
||||||
Parent: s,
|
Parent: s,
|
||||||
@@ -150,6 +156,7 @@ func (s *Handler) Initialize() {
|
|||||||
case strings.HasPrefix(s.Conf.Source, "udp+rtp://") ||
|
case strings.HasPrefix(s.Conf.Source, "udp+rtp://") ||
|
||||||
strings.HasPrefix(s.Conf.Source, "unix+rtp://"):
|
strings.HasPrefix(s.Conf.Source, "unix+rtp://"):
|
||||||
s.instance = &ssrtp.Source{
|
s.instance = &ssrtp.Source{
|
||||||
|
DumpPackets: s.DumpPackets,
|
||||||
ReadTimeout: s.ReadTimeout,
|
ReadTimeout: s.ReadTimeout,
|
||||||
UDPReadBufferSize: s.UDPReadBufferSize,
|
UDPReadBufferSize: s.UDPReadBufferSize,
|
||||||
Parent: s,
|
Parent: s,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
package hls
|
package hls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -14,6 +15,7 @@ import (
|
|||||||
"github.com/bluenviron/mediamtx/internal/defs"
|
"github.com/bluenviron/mediamtx/internal/defs"
|
||||||
"github.com/bluenviron/mediamtx/internal/errordumper"
|
"github.com/bluenviron/mediamtx/internal/errordumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/packetdumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/hls"
|
"github.com/bluenviron/mediamtx/internal/protocols/hls"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/tls"
|
"github.com/bluenviron/mediamtx/internal/protocols/tls"
|
||||||
"github.com/bluenviron/mediamtx/internal/stream"
|
"github.com/bluenviron/mediamtx/internal/stream"
|
||||||
@@ -27,6 +29,7 @@ type parent interface {
|
|||||||
|
|
||||||
// Source is a HLS static source.
|
// Source is a HLS static source.
|
||||||
type Source struct {
|
type Source struct {
|
||||||
|
DumpPackets bool
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
Parent parent
|
Parent parent
|
||||||
}
|
}
|
||||||
@@ -64,7 +67,17 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialContext := (&net.Dialer{}).DialContext
|
||||||
|
|
||||||
|
if s.DumpPackets {
|
||||||
|
dialContext = (&packetdumper.DialContext{
|
||||||
|
Prefix: "hls_source_conn",
|
||||||
|
DialContext: dialContext,
|
||||||
|
}).Do
|
||||||
|
}
|
||||||
|
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
|
DialContext: dialContext,
|
||||||
TLSClientConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
TLSClientConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
||||||
}
|
}
|
||||||
defer tr.CloseIdleConnections()
|
defer tr.CloseIdleConnections()
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/bluenviron/mediamtx/internal/defs"
|
"github.com/bluenviron/mediamtx/internal/defs"
|
||||||
"github.com/bluenviron/mediamtx/internal/errordumper"
|
"github.com/bluenviron/mediamtx/internal/errordumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/packetdumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/mpegts"
|
"github.com/bluenviron/mediamtx/internal/protocols/mpegts"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/udp"
|
"github.com/bluenviron/mediamtx/internal/protocols/udp"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/unix"
|
"github.com/bluenviron/mediamtx/internal/protocols/unix"
|
||||||
@@ -27,6 +28,7 @@ type parent interface {
|
|||||||
|
|
||||||
// Source is a MPEG-TS static source.
|
// Source is a MPEG-TS static source.
|
||||||
type Source struct {
|
type Source struct {
|
||||||
|
DumpPackets bool
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
UDPReadBufferSize uint
|
UDPReadBufferSize uint
|
||||||
Parent parent
|
Parent parent
|
||||||
@@ -50,10 +52,15 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|||||||
|
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case "unix+mpegts":
|
case "unix+mpegts":
|
||||||
nc, err = unix.Listen(u)
|
params := unix.URLToParams(u)
|
||||||
|
l := &unix.Listener{
|
||||||
|
Path: params.Path,
|
||||||
|
}
|
||||||
|
err = l.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
nc = l
|
||||||
|
|
||||||
default:
|
default:
|
||||||
udpReadBufferSize := s.UDPReadBufferSize
|
udpReadBufferSize := s.UDPReadBufferSize
|
||||||
@@ -61,10 +68,41 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|||||||
udpReadBufferSize = *params.Conf.MPEGTSUDPReadBufferSize
|
udpReadBufferSize = *params.Conf.MPEGTSUDPReadBufferSize
|
||||||
}
|
}
|
||||||
|
|
||||||
nc, err = udp.Listen(u, int(udpReadBufferSize))
|
listenPacket := net.ListenPacket
|
||||||
|
|
||||||
|
if s.DumpPackets {
|
||||||
|
listenPacket = func(network, address string) (net.PacketConn, error) {
|
||||||
|
pc, err2 := net.ListenPacket(network, address)
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &packetdumper.PacketConn{
|
||||||
|
Prefix: "mpegts_source_packetconn",
|
||||||
|
PacketConn: pc,
|
||||||
|
}
|
||||||
|
err2 = d.Initialize()
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := udp.URLToParams(u)
|
||||||
|
l := &udp.Listener{
|
||||||
|
Address: params.Address,
|
||||||
|
Source: params.Source,
|
||||||
|
IntfName: params.IntfName,
|
||||||
|
UDPReadBufferSize: int(udpReadBufferSize),
|
||||||
|
ListenPacket: listenPacket,
|
||||||
|
}
|
||||||
|
err = l.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
nc = l
|
||||||
}
|
}
|
||||||
|
|
||||||
readerErr := make(chan error)
|
readerErr := make(chan error)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/bluenviron/mediamtx/internal/conf"
|
"github.com/bluenviron/mediamtx/internal/conf"
|
||||||
"github.com/bluenviron/mediamtx/internal/defs"
|
"github.com/bluenviron/mediamtx/internal/defs"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/packetdumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp"
|
"github.com/bluenviron/mediamtx/internal/protocols/rtmp"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/tls"
|
"github.com/bluenviron/mediamtx/internal/protocols/tls"
|
||||||
"github.com/bluenviron/mediamtx/internal/stream"
|
"github.com/bluenviron/mediamtx/internal/stream"
|
||||||
@@ -27,6 +28,7 @@ type parent interface {
|
|||||||
|
|
||||||
// Source is a RTMP static source.
|
// Source is a RTMP static source.
|
||||||
type Source struct {
|
type Source struct {
|
||||||
|
DumpPackets bool
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
WriteTimeout conf.Duration
|
WriteTimeout conf.Duration
|
||||||
Parent parent
|
Parent parent
|
||||||
@@ -56,11 +58,22 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialContext := (&net.Dialer{}).DialContext
|
||||||
|
|
||||||
|
if s.DumpPackets {
|
||||||
|
dialContext = (&packetdumper.DialContext{
|
||||||
|
Prefix: "rtmp_source_conn",
|
||||||
|
DialContext: dialContext,
|
||||||
|
}).Do
|
||||||
|
}
|
||||||
|
|
||||||
connectCtx, connectCtxCancel := context.WithTimeout(params.Context, time.Duration(s.ReadTimeout))
|
connectCtx, connectCtxCancel := context.WithTimeout(params.Context, time.Duration(s.ReadTimeout))
|
||||||
|
|
||||||
conn := &gortmplib.Client{
|
conn := &gortmplib.Client{
|
||||||
URL: u,
|
URL: u,
|
||||||
TLSConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
TLSConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
||||||
Publish: false,
|
Publish: false,
|
||||||
|
DialContext: dialContext,
|
||||||
}
|
}
|
||||||
err = conn.Initialize(connectCtx)
|
err = conn.Initialize(connectCtx)
|
||||||
connectCtxCancel()
|
connectCtxCancel()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/bluenviron/mediamtx/internal/defs"
|
"github.com/bluenviron/mediamtx/internal/defs"
|
||||||
"github.com/bluenviron/mediamtx/internal/errordumper"
|
"github.com/bluenviron/mediamtx/internal/errordumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/packetdumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/udp"
|
"github.com/bluenviron/mediamtx/internal/protocols/udp"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/unix"
|
"github.com/bluenviron/mediamtx/internal/protocols/unix"
|
||||||
"github.com/bluenviron/mediamtx/internal/stream"
|
"github.com/bluenviron/mediamtx/internal/stream"
|
||||||
@@ -30,6 +31,7 @@ type parent interface {
|
|||||||
|
|
||||||
// Source is a RTP static source.
|
// Source is a RTP static source.
|
||||||
type Source struct {
|
type Source struct {
|
||||||
|
DumpPackets bool
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
UDPReadBufferSize uint
|
UDPReadBufferSize uint
|
||||||
Parent parent
|
Parent parent
|
||||||
@@ -65,10 +67,15 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|||||||
|
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case "unix+rtp":
|
case "unix+rtp":
|
||||||
nc, err = unix.Listen(u)
|
params := unix.URLToParams(u)
|
||||||
|
l := &unix.Listener{
|
||||||
|
Path: params.Path,
|
||||||
|
}
|
||||||
|
err = l.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
nc = l
|
||||||
|
|
||||||
default:
|
default:
|
||||||
udpReadBufferSize := s.UDPReadBufferSize
|
udpReadBufferSize := s.UDPReadBufferSize
|
||||||
@@ -76,10 +83,41 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|||||||
udpReadBufferSize = *params.Conf.RTPUDPReadBufferSize
|
udpReadBufferSize = *params.Conf.RTPUDPReadBufferSize
|
||||||
}
|
}
|
||||||
|
|
||||||
nc, err = udp.Listen(u, int(udpReadBufferSize))
|
listenPacket := net.ListenPacket
|
||||||
|
|
||||||
|
if s.DumpPackets {
|
||||||
|
listenPacket = func(network, address string) (net.PacketConn, error) {
|
||||||
|
pc, err2 := net.ListenPacket(network, address)
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &packetdumper.PacketConn{
|
||||||
|
Prefix: "rtp_source_packetconn",
|
||||||
|
PacketConn: pc,
|
||||||
|
}
|
||||||
|
err2 = d.Initialize()
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := udp.URLToParams(u)
|
||||||
|
l := &udp.Listener{
|
||||||
|
Address: params.Address,
|
||||||
|
Source: params.Source,
|
||||||
|
IntfName: params.IntfName,
|
||||||
|
UDPReadBufferSize: int(udpReadBufferSize),
|
||||||
|
ListenPacket: listenPacket,
|
||||||
|
}
|
||||||
|
err = l.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
nc = l
|
||||||
}
|
}
|
||||||
|
|
||||||
readerErr := make(chan error)
|
readerErr := make(chan error)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package rtsp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
@@ -17,6 +18,7 @@ import (
|
|||||||
"github.com/bluenviron/mediamtx/internal/defs"
|
"github.com/bluenviron/mediamtx/internal/defs"
|
||||||
"github.com/bluenviron/mediamtx/internal/errordumper"
|
"github.com/bluenviron/mediamtx/internal/errordumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/packetdumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/rtsp"
|
"github.com/bluenviron/mediamtx/internal/protocols/rtsp"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/tls"
|
"github.com/bluenviron/mediamtx/internal/protocols/tls"
|
||||||
"github.com/bluenviron/mediamtx/internal/stream"
|
"github.com/bluenviron/mediamtx/internal/stream"
|
||||||
@@ -75,6 +77,7 @@ type parent interface {
|
|||||||
|
|
||||||
// Source is a RTSP static source.
|
// Source is a RTSP static source.
|
||||||
type Source struct {
|
type Source struct {
|
||||||
|
DumpPackets bool
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
WriteTimeout conf.Duration
|
WriteTimeout conf.Duration
|
||||||
WriteQueueSize int
|
WriteQueueSize int
|
||||||
@@ -132,36 +135,20 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|||||||
scheme = "rtsps"
|
scheme = "rtsps"
|
||||||
}
|
}
|
||||||
|
|
||||||
var tunnel gortsplib.Tunnel
|
|
||||||
switch u0.Scheme {
|
|
||||||
case "rtsp+http", "rtsps+http":
|
|
||||||
tunnel = gortsplib.TunnelHTTP
|
|
||||||
case "rtsp+ws", "rtsps+ws":
|
|
||||||
tunnel = gortsplib.TunnelWebSocket
|
|
||||||
default:
|
|
||||||
tunnel = gortsplib.TunnelNone
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := base.ParseURL(regexp.MustCompile("^.*?://").ReplaceAllString(params.ResolvedSource, "rtsp://"))
|
u, err := base.ParseURL(regexp.MustCompile("^.*?://").ReplaceAllString(params.ResolvedSource, "rtsp://"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
udpReadBufferSize := s.UDPReadBufferSize
|
|
||||||
if params.Conf.RTSPUDPReadBufferSize != nil {
|
|
||||||
udpReadBufferSize = *params.Conf.RTSPUDPReadBufferSize
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &gortsplib.Client{
|
c := &gortsplib.Client{
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
Host: u.Host,
|
Host: u.Host,
|
||||||
Tunnel: tunnel,
|
|
||||||
Protocol: params.Conf.RTSPTransport.Protocol,
|
Protocol: params.Conf.RTSPTransport.Protocol,
|
||||||
TLSConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
TLSConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
||||||
ReadTimeout: time.Duration(s.ReadTimeout),
|
ReadTimeout: time.Duration(s.ReadTimeout),
|
||||||
WriteTimeout: time.Duration(s.WriteTimeout),
|
WriteTimeout: time.Duration(s.WriteTimeout),
|
||||||
|
UDPReadBufferSize: int(s.UDPReadBufferSize),
|
||||||
WriteQueueSize: s.WriteQueueSize,
|
WriteQueueSize: s.WriteQueueSize,
|
||||||
UDPReadBufferSize: int(udpReadBufferSize),
|
|
||||||
AnyPortEnable: params.Conf.RTSPAnyPort,
|
AnyPortEnable: params.Conf.RTSPAnyPort,
|
||||||
UDPSourcePortRange: [2]uint16{
|
UDPSourcePortRange: [2]uint16{
|
||||||
uint16(params.Conf.RTSPUDPSourcePortRange[0]),
|
uint16(params.Conf.RTSPUDPSourcePortRange[0]),
|
||||||
@@ -184,6 +171,29 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch u0.Scheme {
|
||||||
|
case "rtsp+http", "rtsps+http":
|
||||||
|
c.Tunnel = gortsplib.TunnelHTTP
|
||||||
|
case "rtsp+ws", "rtsps+ws":
|
||||||
|
c.Tunnel = gortsplib.TunnelWebSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.Conf.RTSPUDPReadBufferSize != nil {
|
||||||
|
s.UDPReadBufferSize = *params.Conf.RTSPUDPReadBufferSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.DumpPackets {
|
||||||
|
c.DialContext = (&packetdumper.DialContext{
|
||||||
|
Prefix: "rtsp_source_conn",
|
||||||
|
DialContext: (&net.Dialer{}).DialContext,
|
||||||
|
}).Do
|
||||||
|
|
||||||
|
c.ListenPacket = (&packetdumper.ListenPacket{
|
||||||
|
Prefix: "rtsp_source_packetconn",
|
||||||
|
ListenPacket: net.ListenPacket,
|
||||||
|
}).Do
|
||||||
|
}
|
||||||
|
|
||||||
err = c.Start()
|
err = c.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package webrtc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
"github.com/bluenviron/mediamtx/internal/conf"
|
"github.com/bluenviron/mediamtx/internal/conf"
|
||||||
"github.com/bluenviron/mediamtx/internal/defs"
|
"github.com/bluenviron/mediamtx/internal/defs"
|
||||||
"github.com/bluenviron/mediamtx/internal/logger"
|
"github.com/bluenviron/mediamtx/internal/logger"
|
||||||
|
"github.com/bluenviron/mediamtx/internal/packetdumper"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/tls"
|
"github.com/bluenviron/mediamtx/internal/protocols/tls"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/webrtc"
|
"github.com/bluenviron/mediamtx/internal/protocols/webrtc"
|
||||||
"github.com/bluenviron/mediamtx/internal/protocols/whip"
|
"github.com/bluenviron/mediamtx/internal/protocols/whip"
|
||||||
@@ -27,6 +29,7 @@ type parent interface {
|
|||||||
|
|
||||||
// Source is a WebRTC static source.
|
// Source is a WebRTC static source.
|
||||||
type Source struct {
|
type Source struct {
|
||||||
|
DumpPackets bool
|
||||||
ReadTimeout conf.Duration
|
ReadTimeout conf.Duration
|
||||||
UDPReadBufferSize uint
|
UDPReadBufferSize uint
|
||||||
Parent parent
|
Parent parent
|
||||||
@@ -48,7 +51,17 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|||||||
|
|
||||||
u.Scheme = strings.ReplaceAll(u.Scheme, "whep", "http")
|
u.Scheme = strings.ReplaceAll(u.Scheme, "whep", "http")
|
||||||
|
|
||||||
|
dialContext := (&net.Dialer{}).DialContext
|
||||||
|
|
||||||
|
if s.DumpPackets {
|
||||||
|
dialContext = (&packetdumper.DialContext{
|
||||||
|
Prefix: "webrtc_source_conn",
|
||||||
|
DialContext: dialContext,
|
||||||
|
}).Do
|
||||||
|
}
|
||||||
|
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
|
DialContext: dialContext,
|
||||||
TLSClientConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
TLSClientConfig: tls.MakeConfig(u.Hostname(), params.Conf.SourceFingerprint),
|
||||||
}
|
}
|
||||||
defer tr.CloseIdleConnections()
|
defer tr.CloseIdleConnections()
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ logStructured: false
|
|||||||
logFile: mediamtx.log
|
logFile: mediamtx.log
|
||||||
# When "syslog" is in logDestinations, use prefix for logs.
|
# When "syslog" is in logDestinations, use prefix for logs.
|
||||||
sysLogPrefix: mediamtx
|
sysLogPrefix: mediamtx
|
||||||
|
# Dump packets to disk. This is useful for debugging.
|
||||||
|
dumpPackets: false
|
||||||
|
|
||||||
# Timeout of read operations.
|
# Timeout of read operations.
|
||||||
readTimeout: 10s
|
readTimeout: 10s
|
||||||
|
|||||||
Reference in New Issue
Block a user