@@ -55,7 +55,7 @@ pub struct OvpnFile {
5555 pub auth : Option < String > ,
5656
5757 // compress directive. Either enabled or specifies algorithm (e.g. "lz4").
58- pub compress : Option < String > ,
58+ pub compress : Option < Compress > ,
5959
6060 // OpenVPN 2.5+ specifies a allow-compress directive for safety
6161 // https://community.openvpn.net/Security%20Announcements/VORACLE
@@ -98,6 +98,13 @@ pub struct TlsAuth {
9898 pub key_direction : Option < u8 > ,
9999}
100100
101+ #[ derive( Debug , Clone ) ]
102+ pub enum Compress {
103+ Stub ,
104+ StubV2 ,
105+ Algorithm ( String ) ,
106+ }
107+
101108#[ derive( Debug , Clone ) ]
102109pub enum AllowCompress {
103110 Yes ,
@@ -127,6 +134,51 @@ enum OvpnItem {
127134 Block { key : String , content : String } ,
128135}
129136
137+ #[ derive( Default ) ]
138+ struct OvpnFileBuilder {
139+ remotes : Vec < Remote > ,
140+ dev : Option < String > ,
141+ proto : Option < String > ,
142+ ca : Option < CertSource > ,
143+ cert : Option < CertSource > ,
144+ key : Option < CertSource > ,
145+ tls_auth : Option < TlsAuth > ,
146+ tls_crypt : Option < CertSource > ,
147+ cipher : Option < String > ,
148+ data_ciphers : Vec < String > ,
149+ auth : Option < String > ,
150+ compress : Option < Compress > ,
151+ allow_compress : Option < AllowCompress > ,
152+ routes : Vec < Route > ,
153+ redirect_gateway : Option < RedirectGateway > ,
154+ flags : Vec < String > ,
155+ options : HashMap < String , Vec < String > > ,
156+ }
157+
158+ impl OvpnFileBuilder {
159+ fn build ( self ) -> OvpnFile {
160+ OvpnFile {
161+ remotes : self . remotes ,
162+ dev : self . dev ,
163+ proto : self . proto ,
164+ ca : self . ca ,
165+ cert : self . cert ,
166+ key : self . key ,
167+ tls_auth : self . tls_auth ,
168+ tls_crypt : self . tls_crypt ,
169+ cipher : self . cipher ,
170+ data_ciphers : self . data_ciphers ,
171+ auth : self . auth ,
172+ compress : self . compress ,
173+ allow_compress : self . allow_compress ,
174+ routes : self . routes ,
175+ redirect_gateway : self . redirect_gateway ,
176+ flags : self . flags ,
177+ options : self . options ,
178+ }
179+ }
180+ }
181+
130182fn lexer ( input : & str ) -> Result < Vec < OvpnItem > , OvpnParseError > {
131183 let mut items = Vec :: new ( ) ;
132184
@@ -262,24 +314,7 @@ fn lexer(input: &str) -> Result<Vec<OvpnItem>, OvpnParseError> {
262314}
263315
264316pub fn parse_ovpn ( content : & str ) -> Result < OvpnFile , ConnectionError > {
265- let mut remotes: Vec < Remote > = Vec :: new ( ) ;
266- let mut dev: Option < String > = None ;
267- let mut proto: Option < String > = None ;
268- let mut ca: Option < CertSource > = None ;
269- let mut cert: Option < CertSource > = None ;
270- let mut key: Option < CertSource > = None ;
271- let mut tls_auth: Option < TlsAuth > = None ;
272- let mut tls_crypt: Option < CertSource > = None ;
273- let mut cipher: Option < String > = None ;
274- let mut data_ciphers: Vec < String > = Vec :: new ( ) ;
275- let mut auth: Option < String > = None ;
276- let mut compress: Option < String > = None ;
277- let mut allow_compress: Option < AllowCompress > = None ;
278- let mut routes: Vec < Route > = Vec :: new ( ) ;
279- let mut redirect_gateway: Option < RedirectGateway > = None ;
280- let mut flags: Vec < String > = Vec :: new ( ) ;
281- let mut options: HashMap < String , Vec < String > > = HashMap :: new ( ) ;
282-
317+ let mut b = OvpnFileBuilder :: default ( ) ;
283318 let items = lexer ( content) ?;
284319
285320 for item in items {
@@ -304,7 +339,7 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
304339
305340 let proto = args. get ( 2 ) . cloned ( ) ;
306341
307- remotes. push ( Remote { host, port, proto } ) ;
342+ b . remotes . push ( Remote { host, port, proto } ) ;
308343 }
309344 "dev" => {
310345 // dev <DEVICE>
@@ -323,7 +358,7 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
323358 line : 0 ,
324359 } ) ?;
325360
326- dev = Some ( value. clone ( ) ) ;
361+ b . dev = Some ( value. clone ( ) ) ;
327362 }
328363 "proto" => {
329364 // proto <PROTOCOL>
@@ -334,7 +369,7 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
334369 line : 0 ,
335370 } ) ?;
336371
337- proto = Some ( value. clone ( ) ) ;
372+ b . proto = Some ( value. clone ( ) ) ;
338373 }
339374 "cipher" => {
340375 // cipher <CIPHER>
@@ -346,7 +381,7 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
346381 line : 0 ,
347382 } ) ?;
348383
349- cipher = Some ( value. clone ( ) ) ;
384+ b . cipher = Some ( value. clone ( ) ) ;
350385 }
351386 "data-ciphers" => {
352387 // data-ciphers <[cipher1]:[cipher2]...>
@@ -357,7 +392,7 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
357392 line : 0 ,
358393 } ) ?;
359394
360- data_ciphers. extend ( ciphers. split ( ':' ) . map ( String :: from) ) ;
395+ b . data_ciphers . extend ( ciphers. split ( ':' ) . map ( String :: from) ) ;
361396 }
362397 "auth" => {
363398 // auth <ALGORITHM>
@@ -368,18 +403,16 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
368403 line : 0 ,
369404 } ) ?;
370405
371- auth = Some ( value. clone ( ) ) ;
406+ b . auth = Some ( value. clone ( ) ) ;
372407 }
373408 "compress" => {
374- // compress <ALGORITHM>
375-
376- let value = args. get ( 0 ) . ok_or ( OvpnParseError :: InvalidArgument {
377- key,
378- arg : "" . into ( ) ,
379- line : 0 ,
380- } ) ?;
409+ // compress [ALGORITHM]
381410
382- compress = Some ( value. clone ( ) ) ;
411+ b. compress = Some ( match args. first ( ) . map ( |s| s. as_str ( ) ) {
412+ None | Some ( "stub" ) => Compress :: Stub ,
413+ Some ( "stub-v2" ) => Compress :: StubV2 ,
414+ Some ( alg) => Compress :: Algorithm ( alg. to_string ( ) ) ,
415+ } ) ;
383416 }
384417 "allow-compress" => {
385418 // allow-compress asym (default) <- receive compressed data but don't send
@@ -398,7 +431,7 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
398431 other => AllowCompress :: Other ( other. to_string ( ) ) ,
399432 } ;
400433
401- allow_compress = Some ( parsed) ;
434+ b . allow_compress = Some ( parsed) ;
402435 }
403436 "route" => {
404437 // route <NETWORK> [NETMASK] [GATEWAY]
@@ -413,7 +446,7 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
413446 . map ( |v| parse_ipv4_arg ( & key, Some ( v) , 0 ) )
414447 . transpose ( ) ?;
415448
416- routes. push ( Route {
449+ b . routes . push ( Route {
417450 network,
418451 netmask,
419452 gateway,
@@ -439,13 +472,13 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
439472 }
440473 }
441474
442- redirect_gateway = Some ( rg) ;
475+ b . redirect_gateway = Some ( rg) ;
443476 }
444477 _ => {
445478 if args. is_empty ( ) {
446- flags. push ( key) ;
479+ b . flags . push ( key) ;
447480 } else {
448- options. entry ( key) . or_default ( ) . extend ( args) ;
481+ b . options . entry ( key) . or_default ( ) . extend ( args) ;
449482 }
450483 }
451484 }
@@ -456,55 +489,37 @@ pub fn parse_ovpn(content: &str) -> Result<OvpnFile, ConnectionError> {
456489 } => {
457490 match block_key. as_str ( ) {
458491 "ca" => {
459- ca = Some ( CertSource :: Inline ( content) ) ;
492+ b . ca = Some ( CertSource :: Inline ( content) ) ;
460493 }
461494
462495 "cert" => {
463- cert = Some ( CertSource :: Inline ( content) ) ;
496+ b . cert = Some ( CertSource :: Inline ( content) ) ;
464497 }
465498
466499 "key" => {
467- key = Some ( CertSource :: Inline ( content) ) ;
500+ b . key = Some ( CertSource :: Inline ( content) ) ;
468501 }
469502
470503 "tls-auth" => {
471- tls_auth = Some ( TlsAuth {
504+ b . tls_auth = Some ( TlsAuth {
472505 source : CertSource :: Inline ( content) ,
473506 key_direction : None , // FIXME: handle seperately
474507 } ) ;
475508 }
476509
477510 "tls-crypt" => {
478- tls_crypt = Some ( CertSource :: Inline ( content) ) ;
511+ b . tls_crypt = Some ( CertSource :: Inline ( content) ) ;
479512 }
480513
481514 _ => {
482- options. entry ( block_key) . or_default ( ) . push ( content) ;
515+ b . options . entry ( block_key) . or_default ( ) . push ( content) ;
483516 }
484517 }
485518 }
486519 }
487520 }
488521
489- Ok ( OvpnFile {
490- remotes,
491- dev,
492- proto,
493- ca,
494- cert,
495- key,
496- tls_auth,
497- tls_crypt,
498- cipher,
499- data_ciphers,
500- auth,
501- compress,
502- allow_compress,
503- routes,
504- redirect_gateway,
505- flags,
506- options,
507- } )
522+ Ok ( b. build ( ) )
508523}
509524
510525fn parse_ipv4_arg (
0 commit comments