@@ -7,12 +7,14 @@ import (
77 "time"
88
99 "github.com/Ullaakut/cameradar/v6"
10+ "github.com/bluenviron/gortsplib/v5"
1011 "github.com/bluenviron/gortsplib/v5/pkg/base"
12+ "github.com/bluenviron/gortsplib/v5/pkg/description"
1113 "github.com/bluenviron/gortsplib/v5/pkg/liberrors"
1214)
1315
1416// Route that should never be a constructor default.
15- const dummyRoute = "/ 0x8b6c42"
17+ const dummyRoute = "0x8b6c42"
1618
1719// Dictionary provides dictionaries for routes, usernames and passwords.
1820type Dictionary interface {
@@ -187,6 +189,7 @@ func (a Attacker) reattackRoutes(ctx context.Context, streams []cameradar.Stream
187189func needsReattack (streams []cameradar.Stream ) bool {
188190 for _ , stream := range streams {
189191 if stream .RouteFound && stream .CredentialsFound && stream .Available {
192+ // This stream is fully discovered, no need to re-attack.
190193 continue
191194 }
192195 return true
@@ -257,7 +260,7 @@ func (a Attacker) attackRoutesForStream(ctx context.Context, target cameradar.St
257260 }
258261 if ok {
259262 target .RouteFound = true
260- target .Routes = append (target .Routes , "/" )
263+ target .Routes = append (target .Routes , "" ) // Add empty route for default.
261264 a .reporter .Progress (cameradar .StepAttackRoutes , fmt .Sprintf ("Default route accepted for %s:%d" , target .Address .String (), target .Port ))
262265 return target , nil
263266 }
@@ -287,67 +290,6 @@ func (a Attacker) attackRoutesForStream(ctx context.Context, target cameradar.St
287290 return target , nil
288291}
289292
290- func (a Attacker ) detectAuthMethods (ctx context.Context , targets []cameradar.Stream ) ([]cameradar.Stream , error ) {
291- streams , err := runParallel (ctx , targets , a .detectAuthMethod )
292- if err != nil {
293- return streams , err
294- }
295-
296- for i := range streams {
297- a .reporter .Progress (cameradar .StepDetectAuth , cameradar .ProgressTickMessage ())
298-
299- var authMethod string
300- switch streams [i ].AuthenticationType {
301- case cameradar .AuthNone :
302- authMethod = "no"
303- case cameradar .AuthBasic :
304- authMethod = "basic"
305- case cameradar .AuthDigest :
306- authMethod = "digest"
307- default :
308- return streams , fmt .Errorf ("unknown authentication method %d for %s:%d" , streams [i ].AuthenticationType , streams [i ].Address .String (), streams [i ].Port )
309- }
310-
311- a .reporter .Progress (cameradar .StepDetectAuth , fmt .Sprintf ("Detected %s authentication for %s:%d" , authMethod , streams [i ].Address .String (), streams [i ].Port ))
312- }
313-
314- return streams , nil
315- }
316-
317- func (a Attacker ) detectAuthMethod (ctx context.Context , stream cameradar.Stream ) (cameradar.Stream , error ) {
318- if ctx .Err () != nil {
319- return stream , ctx .Err ()
320- }
321- u , urlStr , err := buildRTSPURL (stream , stream .Route (), "" , "" )
322- if err != nil {
323- return stream , fmt .Errorf ("building rtsp url: %w" , err )
324- }
325-
326- client , err := a .newRTSPClient (u )
327- if err != nil {
328- return stream , fmt .Errorf ("starting rtsp client: %w" , err )
329- }
330- defer client .Close ()
331-
332- _ , res , err := client .Describe (u )
333- if err != nil {
334- var badStatus liberrors.ErrClientBadStatusCode
335- if errors .As (err , & badStatus ) && res != nil && badStatus .Code == base .StatusUnauthorized {
336- stream .AuthenticationType = authTypeFromHeaders (res .Header ["WWW-Authenticate" ])
337- a .reporter .Debug (cameradar .StepDetectAuth , fmt .Sprintf ("DESCRIBE %s RTSP/1.0 > %d" , urlStr , badStatus .Code ))
338- return stream , nil
339- }
340- return stream , fmt .Errorf ("performing describe request at %q: %w" , urlStr , err )
341- }
342-
343- if res != nil {
344- a .reporter .Debug (cameradar .StepDetectAuth , fmt .Sprintf ("DESCRIBE %s RTSP/1.0 > %d" , urlStr , res .StatusCode ))
345- }
346-
347- stream .AuthenticationType = cameradar .AuthNone
348- return stream , nil
349- }
350-
351293func (a Attacker ) routeAttack (stream cameradar.Stream , route string ) (bool , error ) {
352294 u , urlStr , err := buildRTSPURL (stream , route , stream .Username , stream .Password )
353295 if err != nil {
@@ -399,7 +341,7 @@ func (a Attacker) validateStream(ctx context.Context, stream cameradar.Stream, e
399341 }
400342 defer client .Close ()
401343
402- desc , res , err := client . Describe ( u )
344+ desc , res , err := a . describeWithRetry ( ctx , client , u , urlStr )
403345 if err != nil {
404346 return a .handleDescribeError (stream , urlStr , err )
405347 }
@@ -413,7 +355,6 @@ func (a Attacker) validateStream(ctx context.Context, stream cameradar.Stream, e
413355 if err != nil {
414356 return a .handleSetupError (stream , urlStr , err )
415357 }
416-
417358 a .logSetupResponse (urlStr , res )
418359
419360 stream .Available = res != nil && res .StatusCode == base .StatusOK
@@ -424,9 +365,39 @@ func (a Attacker) validateStream(ctx context.Context, stream cameradar.Stream, e
424365 return stream , nil
425366}
426367
368+ func (a Attacker ) describeWithRetry (ctx context.Context , client * gortsplib.Client , u * base.URL , urlStr string ) (* description.Session , * base.Response , error ) {
369+ var (
370+ desc * description.Session
371+ res * base.Response
372+ err error
373+ )
374+ for range 5 {
375+ desc , res , err = client .Describe (u )
376+ if err == nil {
377+ return desc , res , nil
378+ }
379+
380+ var badStatus liberrors.ErrClientBadStatusCode
381+ if errors .As (err , & badStatus ) && badStatus .Code == base .StatusServiceUnavailable {
382+ a .reporter .Debug (cameradar .StepValidateStreams , fmt .Sprintf ("DESCRIBE %s RTSP/1.0 > %d (retrying)" , urlStr , badStatus .Code ))
383+ select {
384+ case <- ctx .Done ():
385+ return nil , nil , ctx .Err ()
386+ case <- time .After (time .Second ):
387+ }
388+ continue
389+ }
390+
391+ return nil , nil , err
392+ }
393+
394+ return nil , nil , fmt .Errorf ("describe retries exhausted for %q: %w" , urlStr , err )
395+ }
396+
427397func (a Attacker ) handleDescribeError (stream cameradar.Stream , urlStr string , err error ) (cameradar.Stream , error ) {
428398 var badStatus liberrors.ErrClientBadStatusCode
429399 if errors .As (err , & badStatus ) && badStatus .Code == base .StatusServiceUnavailable {
400+ a .reporter .Debug (cameradar .StepValidateStreams , fmt .Sprintf ("DESCRIBE %s RTSP/1.0 > %d" , urlStr , badStatus .Code ))
430401 a .reporter .Progress (cameradar .StepValidateStreams , fmt .Sprintf ("Stream unavailable for %s:%d (RTSP %d)" ,
431402 stream .Address .String (),
432403 stream .Port ,
@@ -436,6 +407,8 @@ func (a Attacker) handleDescribeError(stream cameradar.Stream, urlStr string, er
436407 return stream , nil
437408 }
438409
410+ a .reporter .Debug (cameradar .StepValidateStreams , fmt .Sprintf ("DESCRIBE %s RTSP/1.0 > error: %v" , urlStr , err ))
411+
439412 return stream , fmt .Errorf ("performing describe request at %q: %w" , urlStr , err )
440413}
441414
0 commit comments