1 /*
2 * SRT - Secure, Reliable, Transport
3 * Copyright (c) 2018 Haivision Systems Inc.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 */
10
11 #ifndef INC_SRT_SOCKETOPTIONS_HPP
12 #define INC_SRT_SOCKETOPTIONS_HPP
13
14 #include <string>
15 #include <map>
16 #include <set>
17 #include <vector>
18 #include "../srtcore/srt.h" // Devel path
19
20 #ifdef _WIN32
21 #include "winsock2.h"
22 #endif
23
24 struct OptionValue
25 {
26 std::string s;
27 union {
28 int i;
29 int64_t l;
30 bool b;
31 };
32
33 const void* value = nullptr;
34 size_t size = 0;
35 };
36
37 extern const std::set<std::string> false_names, true_names;
38
39 struct SocketOption
40 {
41 enum Type { STRING = 0, INT, INT64, BOOL, ENUM };
42 enum Binding { PRE = 0, POST };
43 enum Domain { SYSTEM, SRT };
44 enum Mode {FAILURE = -1, LISTENER = 0, CALLER = 1, RENDEZVOUS = 2};
45 static const char* const mode_names [3];
46
47 std::string name;
48 int protocol;
49 int symbol;
50 Binding binding;
51 Type type;
52 const std::map<std::string, int>* valmap;
53
54 template <Domain D, typename Object = int>
55 bool apply(Object socket, std::string value) const;
56
57 template <Domain D, Type T, typename Object = int>
58 bool applyt(Object socket, std::string value) const;
59
60 template <Domain D, typename Object>
61 static int setso(Object socket, int protocol, int symbol, const void* data, size_t size);
62
63 template<Type T>
64 bool extract(std::string value, OptionValue& val) const;
65 };
66
67 template<>
setso(int socket,int,int sym,const void * data,size_t size)68 inline int SocketOption::setso<SocketOption::SRT, int>(int socket, int /*ignored*/, int sym, const void* data, size_t size)
69 {
70 return srt_setsockopt(socket, 0, SRT_SOCKOPT(sym), data, (int) size);
71 }
72
73 #if ENABLE_EXPERIMENTAL_BONDING
74 template<>
setso(SRT_SOCKOPT_CONFIG * obj,int,int sym,const void * data,size_t size)75 inline int SocketOption::setso<SocketOption::SRT, SRT_SOCKOPT_CONFIG*>(SRT_SOCKOPT_CONFIG* obj, int /*ignored*/, int sym, const void* data, size_t size)
76 {
77 return srt_config_add(obj, SRT_SOCKOPT(sym), data, (int) size);
78 }
79 #endif
80
81
82 template<>
setso(int socket,int proto,int sym,const void * data,size_t size)83 inline int SocketOption::setso<SocketOption::SYSTEM, int>(int socket, int proto, int sym, const void* data, size_t size)
84 {
85 return ::setsockopt(socket, proto, sym, (const char *)data, (int) size);
86 }
87
88 template<>
extract(std::string value,OptionValue & o) const89 inline bool SocketOption::extract<SocketOption::STRING>(std::string value, OptionValue& o) const
90 {
91 o.s = value;
92 o.value = o.s.data();
93 o.size = o.s.size();
94 return true;
95 }
96
97 template<>
extract(std::string value,OptionValue & o) const98 inline bool SocketOption::extract<SocketOption::INT>(std::string value, OptionValue& o) const
99 {
100 try
101 {
102 o.i = stoi(value, 0, 0);
103 o.value = &o.i;
104 o.size = sizeof o.i;
105 return true;
106 }
107 catch (...) // stoi throws
108 {
109 return false; // do not change o
110 }
111 return false;
112 }
113
114 template<>
extract(std::string value,OptionValue & o) const115 inline bool SocketOption::extract<SocketOption::INT64>(std::string value, OptionValue& o) const
116 {
117 try
118 {
119 long long vall = stoll(value);
120 o.l = vall; // int64_t resolves to either 'long long', or 'long' being 64-bit integer
121 o.value = &o.l;
122 o.size = sizeof o.l;
123 return true;
124 }
125 catch (...) // stoll throws
126 {
127 return false;
128 }
129 return false;
130 }
131
132 template<>
extract(std::string value,OptionValue & o) const133 inline bool SocketOption::extract<SocketOption::BOOL>(std::string value, OptionValue& o) const
134 {
135 bool val;
136 if ( false_names.count(value) )
137 val = false;
138 else if ( true_names.count(value) )
139 val = true;
140 else
141 return false;
142
143 o.b = val;
144 o.value = &o.b;
145 o.size = sizeof o.b;
146 return true;
147 }
148
149 template<>
extract(std::string value,OptionValue & o) const150 inline bool SocketOption::extract<SocketOption::ENUM>(std::string value, OptionValue& o) const
151 {
152 if (valmap)
153 {
154 // Search value in the map. If found, set to o.
155 auto p = valmap->find(value);
156 if ( p != valmap->end() )
157 {
158 o.i = p->second;
159 o.value = &o.i;
160 o.size = sizeof o.i;
161 return true;
162 }
163 }
164
165 // Fallback: try interpreting it as integer.
166 try
167 {
168 o.i = stoi(value, 0, 0);
169 o.value = &o.i;
170 o.size = sizeof o.i;
171 return true;
172 }
173 catch (...) // stoi throws
174 {
175 return false; // do not change o
176 }
177 return false;
178 }
179
180 template <SocketOption::Domain D, SocketOption::Type T, typename Object>
applyt(Object socket,std::string value) const181 inline bool SocketOption::applyt(Object socket, std::string value) const
182 {
183 OptionValue o; // common meet point
184 int result = -1;
185 if (extract<T>(value, o))
186 result = setso<D>(socket, protocol, symbol, o.value, o.size);
187 return result != -1;
188 }
189
190
191 template<SocketOption::Domain D, typename Object>
apply(Object socket,std::string value) const192 inline bool SocketOption::apply(Object socket, std::string value) const
193 {
194 switch ( type )
195 {
196 #define SRT_HANDLE_TYPE(ty) case ty: return applyt<D, ty, Object>(socket, value)
197
198 SRT_HANDLE_TYPE(STRING);
199 SRT_HANDLE_TYPE(INT);
200 SRT_HANDLE_TYPE(INT64);
201 SRT_HANDLE_TYPE(BOOL);
202 SRT_HANDLE_TYPE(ENUM);
203
204 #undef SRT_HANDLE_TYPE
205 }
206 return false;
207 }
208
209 extern const std::map<std::string, int> enummap_transtype;
210
211 namespace {
212 const SocketOption srt_options [] {
213 { "transtype", 0, SRTO_TRANSTYPE, SocketOption::PRE, SocketOption::ENUM, &enummap_transtype },
214 { "maxbw", 0, SRTO_MAXBW, SocketOption::POST, SocketOption::INT64, nullptr},
215 { "pbkeylen", 0, SRTO_PBKEYLEN, SocketOption::PRE, SocketOption::INT, nullptr},
216 { "passphrase", 0, SRTO_PASSPHRASE, SocketOption::PRE, SocketOption::STRING, nullptr},
217
218 { "mss", 0, SRTO_MSS, SocketOption::PRE, SocketOption::INT, nullptr},
219 { "fc", 0, SRTO_FC, SocketOption::PRE, SocketOption::INT, nullptr},
220 { "sndbuf", 0, SRTO_SNDBUF, SocketOption::PRE, SocketOption::INT, nullptr},
221 { "rcvbuf", 0, SRTO_RCVBUF, SocketOption::PRE, SocketOption::INT, nullptr},
222 // linger option is handled outside of the common loop, therefore commented out.
223 //{ "linger", 0, SRTO_LINGER, SocketOption::PRE, SocketOption::INT, nullptr},
224 { "ipttl", 0, SRTO_IPTTL, SocketOption::PRE, SocketOption::INT, nullptr},
225 { "iptos", 0, SRTO_IPTOS, SocketOption::PRE, SocketOption::INT, nullptr},
226 { "inputbw", 0, SRTO_INPUTBW, SocketOption::POST, SocketOption::INT64, nullptr},
227 { "mininputbw", 0, SRTO_MININPUTBW, SocketOption::POST, SocketOption::INT64, nullptr},
228 { "oheadbw", 0, SRTO_OHEADBW, SocketOption::POST, SocketOption::INT, nullptr},
229 { "latency", 0, SRTO_LATENCY, SocketOption::PRE, SocketOption::INT, nullptr},
230 { "tsbpdmode", 0, SRTO_TSBPDMODE, SocketOption::PRE, SocketOption::BOOL, nullptr},
231 { "tlpktdrop", 0, SRTO_TLPKTDROP, SocketOption::PRE, SocketOption::BOOL, nullptr},
232 { "snddropdelay", 0, SRTO_SNDDROPDELAY, SocketOption::POST, SocketOption::INT, nullptr},
233 { "nakreport", 0, SRTO_NAKREPORT, SocketOption::PRE, SocketOption::BOOL, nullptr},
234 { "conntimeo", 0, SRTO_CONNTIMEO, SocketOption::PRE, SocketOption::INT, nullptr},
235 { "drifttracer", 0, SRTO_DRIFTTRACER, SocketOption::POST, SocketOption::BOOL, nullptr},
236 { "lossmaxttl", 0, SRTO_LOSSMAXTTL, SocketOption::POST, SocketOption::INT, nullptr},
237 { "rcvlatency", 0, SRTO_RCVLATENCY, SocketOption::PRE, SocketOption::INT, nullptr},
238 { "peerlatency", 0, SRTO_PEERLATENCY, SocketOption::PRE, SocketOption::INT, nullptr},
239 { "minversion", 0, SRTO_MINVERSION, SocketOption::PRE, SocketOption::INT, nullptr},
240 { "streamid", 0, SRTO_STREAMID, SocketOption::PRE, SocketOption::STRING, nullptr},
241 { "congestion", 0, SRTO_CONGESTION, SocketOption::PRE, SocketOption::STRING, nullptr},
242 { "messageapi", 0, SRTO_MESSAGEAPI, SocketOption::PRE, SocketOption::BOOL, nullptr},
243 { "payloadsize", 0, SRTO_PAYLOADSIZE, SocketOption::PRE, SocketOption::INT, nullptr},
244 { "kmrefreshrate", 0, SRTO_KMREFRESHRATE, SocketOption::PRE, SocketOption::INT, nullptr },
245 { "kmpreannounce", 0, SRTO_KMPREANNOUNCE, SocketOption::PRE, SocketOption::INT, nullptr },
246 { "enforcedencryption", 0, SRTO_ENFORCEDENCRYPTION, SocketOption::PRE, SocketOption::BOOL, nullptr },
247 { "ipv6only", 0, SRTO_IPV6ONLY, SocketOption::PRE, SocketOption::INT, nullptr },
248 { "peeridletimeo", 0, SRTO_PEERIDLETIMEO, SocketOption::PRE, SocketOption::INT, nullptr },
249 { "packetfilter", 0, SRTO_PACKETFILTER, SocketOption::PRE, SocketOption::STRING, nullptr },
250 #if ENABLE_EXPERIMENTAL_BONDING
251 { "groupconnect", 0, SRTO_GROUPCONNECT, SocketOption::PRE, SocketOption::INT, nullptr},
252 #endif
253 #ifdef SRT_ENABLE_BINDTODEVICE
254 { "bindtodevice", 0, SRTO_BINDTODEVICE, SocketOption::PRE, SocketOption::STRING, nullptr},
255 #endif
256 #if ENABLE_EXPERIMENTAL_BONDING
257 { "groupstabtimeo", 0, SRTO_GROUPSTABTIMEO, SocketOption::POST, SocketOption::INT, nullptr},
258 #endif
259 { "retransmitalgo", 0, SRTO_RETRANSMITALGO, SocketOption::PRE, SocketOption::INT, nullptr }
260 };
261 }
262
263 SocketOption::Mode SrtInterpretMode(const std::string& modestr, const std::string& host, const std::string& adapter);
264 SocketOption::Mode SrtConfigurePre(SRTSOCKET socket, std::string host, std::map<std::string, std::string> options, std::vector<std::string>* failures = 0);
265 void SrtConfigurePost(SRTSOCKET socket, std::map<std::string, std::string> options, std::vector<std::string>* failures = 0);
266
267 #endif
268