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