Coverage Report

Created: 2026-02-14 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/header/SipLayer.h
Line
Count
Source
1
#pragma once
2
3
#include "TextBasedProtocol.h"
4
5
/// @file
6
7
/// @namespace pcpp
8
/// @brief The main namespace for the PcapPlusPlus lib
9
namespace pcpp
10
{
11
// some popular SIP header fields
12
13
/// From field
14
#define PCPP_SIP_FROM_FIELD "From"
15
/// To field
16
#define PCPP_SIP_TO_FIELD "To"
17
/// Via field
18
#define PCPP_SIP_VIA_FIELD "Via"
19
/// Call-ID field
20
#define PCPP_SIP_CALL_ID_FIELD "Call-ID"
21
/// Content-Type field
22
7.20k
#define PCPP_SIP_CONTENT_TYPE_FIELD "Content-Type"
23
/// Content-Length field
24
17.1k
#define PCPP_SIP_CONTENT_LENGTH_FIELD "Content-Length"
25
/// Content-Disposition field
26
#define PCPP_SIP_CONTENT_DISPOSITION_FIELD "Content-Disposition"
27
/// Content-Encoding field
28
#define PCPP_SIP_CONTENT_ENCODING_FIELD "Content-Encoding"
29
/// Content-Language field
30
#define PCPP_SIP_CONTENT_LANGUAGE_FIELD "Content-Language"
31
/// CSeq field
32
#define PCPP_SIP_CSEQ_FIELD "CSeq"
33
/// Contact field
34
#define PCPP_SIP_CONTACT_FIELD "Contact"
35
/// Max-Forwards field
36
#define PCPP_SIP_MAX_FORWARDS_FIELD "Max-Forwards"
37
/// User-Agent field
38
#define PCPP_SIP_USER_AGENT_FIELD "User-Agent"
39
/// Accept field
40
#define PCPP_SIP_ACCEPT_FIELD "Accept"
41
/// Accept-Encoding field
42
#define PCPP_SIP_ACCEPT_ENCODING_FIELD "Accept-Encoding"
43
/// Accept-Language field
44
#define PCPP_SIP_ACCEPT_LANGUAGE_FIELD "Accept-Language"
45
/// Allow field
46
#define PCPP_SIP_ALLOW_FIELD "Allow"
47
/// Authorization field
48
#define PCPP_SIP_AUTHORIZATION_FIELD "Authorization"
49
/// Date field
50
#define PCPP_SIP_DATE_FIELD "Date"
51
/// MIME-Version field
52
#define PCPP_SIP_MIME_VERSION_FIELD "MIME-Version"
53
/// Reason field
54
#define PCPP_SIP_REASON_FIELD "Reason"
55
/// Supported field
56
#define PCPP_SIP_SUPPORTED_FIELD "Supported"
57
/// Server field
58
#define PCPP_SIP_SERVER_FIELD "Server"
59
/// WWW-Authenticate fild
60
#define PCPP_SIP_WWW_AUTHENTICATE_FIELD "WWW-Authenticate"
61
/// Retry-After field
62
#define PCPP_SIP_RETRY_AFTER_FIELD "Retry-After"
63
/// Record-Route field
64
#define PCPP_SIP_RECORD_ROUTE_FIELD "Record-Route"
65
66
  /// @class SipLayer
67
  /// Represents a general SIP message. It's an abstract class and cannot be instantiated. It's inherited by
68
  /// SipRequestLayer and SipResponseLayer
69
  class SipLayer : public TextBasedProtocolMessage
70
  {
71
  public:
72
    /// The length of the body of many SIP response messages is determined by a SIP header field called
73
    /// "Content-Length". This method parses this field, extracts its value and return it. If this field doesn't
74
    /// exist 0 is returned
75
    /// @return SIP response body length determined by "Content-Length" field
76
    int getContentLength() const;
77
78
    /// The length of the body of many SIP messages is determined by a header field called "Content-Length". This
79
    /// method sets The content-length field value. The method supports several cases:
80
    /// - If the "Content-Length" field exists - the method will only replace the existing value with the new value
81
    /// - If the "Content-Length" field doesn't exist - the method will create this field and put the value in it.
82
    /// Here are also 2 cases:
83
    ///   - If prevFieldName is specified - the new "Content-Length" field will be created after it
84
    ///   - If prevFieldName isn't specified or doesn't exist - the new "Content-Length" field will be created as
85
    ///   the
86
    /// last field before end-of-header field
87
    ///
88
    /// @param[in] contentLength The content length value to set
89
    /// @param[in] prevFieldName Optional parameter, if specified and "Content-Length" field doesn't exist, it will
90
    /// be created after this field
91
    /// @return A pointer to the "Content-Length" field, or nullptr if creation failed
92
    HeaderField* setContentLength(int contentLength, const std::string& prevFieldName = "");
93
94
    // Overridden methods
95
96
    OsiModelLayer getOsiModelLayer() const override
97
3.92k
    {
98
3.92k
      return OsiModelSesionLayer;
99
3.92k
    }
100
101
    /// Currently identifies only SDP if content-length field exists and set to a value greater than zero.
102
    /// If content-length field doesn't exist or set to zero and still there is data after this layer, a
103
    /// PayloadLayer will be created
104
    void parseNextLayer() override;
105
106
    /// Set the content-length only if a content-length field already exists and if its current value is different
107
    /// than the total length of the next layer(s)
108
    void computeCalculateFields() override;
109
110
    /// A static method that checks whether the port is considered as SIP
111
    /// @param[in] port The port number to be checked
112
    static bool isSipPort(uint16_t port)
113
1.11M
    {
114
1.11M
      return port == 5060 || port == 5061;
115
1.11M
    }
116
117
    /// A static factory method that attempts to create a SIP layer from existing packet raw data
118
    /// The method first checks whether the source or destination port matches the SIP protocol.
119
    /// @param[in] data A pointer to the raw data
120
    /// @param[in] dataLen Size of the data in bytes
121
    /// @param[in] prevLayer A pointer to the previous layer
122
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
123
    /// @param[in] srcPort Source port number to check
124
    /// @param[in] dstPort Dest port number to check
125
    /// @return A newly allocated SIP layer of type request or response, or nullptr if parsing fails
126
    static SipLayer* parseSipLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet,
127
                                   uint16_t srcPort, uint16_t dstPort);
128
129
    /// A static factory method that attempts to create a SIP layer from existing packet raw data
130
    /// This method does not check source or destination ports. Instead, it uses heuristics
131
    /// to determine whether the data represents a SIP request or response.
132
    /// @param[in] data A pointer to the raw data
133
    /// @param[in] dataLen Size of the data in bytes
134
    /// @param[in] prevLayer A pointer to the previous layer
135
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
136
    /// @return A newly allocated SIP layer of type request or response, or nullptr if parsing fails
137
    static SipLayer* parseSipLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
138
139
  protected:
140
    SipLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, ProtocolType protocol)
141
24.9k
        : TextBasedProtocolMessage(data, dataLen, prevLayer, packet, protocol)
142
24.9k
    {}
143
0
    SipLayer() : TextBasedProtocolMessage()
144
0
    {}
145
0
    SipLayer(const SipLayer& other) : TextBasedProtocolMessage(other)
146
0
    {}
147
    SipLayer& operator=(const SipLayer& other)
148
0
    {
149
0
      TextBasedProtocolMessage::operator=(other);
150
0
      return *this;
151
0
    }
152
153
    // implementation of abstract methods
154
    char getHeaderFieldNameValueSeparator() const override
155
24.9k
    {
156
24.9k
      return ':';
157
24.9k
    }
158
    bool spacesAllowedBetweenHeaderFieldNameAndValue() const override
159
24.9k
    {
160
24.9k
      return true;
161
24.9k
    }
162
163
  private:
164
    enum class SipParseResult
165
    {
166
      Unknown = 0,
167
      Request = 1,
168
      Response = 2,
169
    };
170
171
    static SipParseResult detectSipMessageType(const uint8_t* data, size_t dataLen);
172
  };
173
174
  class SipRequestFirstLine;
175
176
  /// @class SipRequestLayer
177
  /// Represents a SIP request header and inherits all basic functionality of SipLayer and TextBasedProtocolMessage.
178
  /// The functionality that is added for this class is the SIP first line concept. A SIP request has the following
179
  /// first line: <i>INVITE sip:bla@bla.com:12345 SIP/2.0</i> Since it's not an "ordinary" header field, it requires a
180
  /// special treatment and gets a class of it's own: SipRequestFirstLine. In most cases a SIP request will be
181
  /// contained in a single packet but for cases it is not, only the first packet will be identified as SIP request
182
  /// layer. You can find out whether the header is complete by using SipLayer#isHeaderComplete()
183
  class SipRequestLayer : public SipLayer
184
  {
185
    friend class SipRequestFirstLine;
186
187
  public:
188
    /// SIP request methods
189
    enum SipMethod
190
    {
191
      /// INVITE
192
      SipINVITE,
193
      /// ACK
194
      SipACK,
195
      /// BYE
196
      SipBYE,
197
      /// CANCEL
198
      SipCANCEL,
199
      /// REFISTER
200
      SipREGISTER,
201
      /// PRACK
202
      SipPRACK,
203
      /// OPTIONS
204
      SipOPTIONS,
205
      /// SUBSCRIBE
206
      SipSUBSCRIBE,
207
      /// NOTIFY
208
      SipNOTIFY,
209
      /// PUBLISH
210
      SipPUBLISH,
211
      /// INFO
212
      SipINFO,
213
      /// REFER
214
      SipREFER,
215
      /// MESSAGE
216
      SipMESSAGE,
217
      /// UPDATE
218
      SipUPDATE,
219
      /// Unknown SIP method
220
      SipMethodUnknown
221
    };
222
223
    /// A constructor that creates the layer from an existing packet raw data
224
    /// @param[in] data A pointer to the raw data
225
    /// @param[in] dataLen Size of the data in bytes
226
    /// @param[in] prevLayer A pointer to the previous layer
227
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
228
    SipRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
229
230
    /// A constructor that allocates a new SIP request with only the first line filled. The request will be created
231
    /// without further fields. The user can then add fields using addField() or insertField() methods
232
    /// @param[in] method The SIP method to be used in this SIP request
233
    /// @param[in] requestUri The URI of the request
234
    /// @param[in] version SIP version to be used in this request. Default is "SIP/2.0"
235
    SipRequestLayer(SipMethod method, const std::string& requestUri, const std::string& version = "SIP/2.0");
236
237
    ~SipRequestLayer() override;
238
239
    /// A copy constructor for this layer. Inherits base copy constructor SipLayer and adds the functionality
240
    /// of copying the first line
241
    /// @param[in] other The instance to copy from
242
    SipRequestLayer(const SipRequestLayer& other);
243
244
    /// An assignment operator overload for this layer. This method inherits base assignment operator
245
    /// SipLayer#operator=() and adds the functionality of copying the first line
246
    /// @param[in] other The instance to copy from
247
    SipRequestLayer& operator=(const SipRequestLayer& other);
248
249
    /// @return A pointer to the first line instance for this message
250
    SipRequestFirstLine* getFirstLine() const
251
0
    {
252
0
      return m_FirstLine;
253
0
    }
254
255
    // implement Layer's abstract methods
256
257
    std::string toString() const override;
258
259
  private:
260
    SipRequestFirstLine* m_FirstLine;
261
  };
262
263
  class SipResponseFirstLine;
264
265
  /// @class SipResponseLayer
266
  /// Represents an SIP response message and inherits all basic functionality of SipLayer and
267
  /// TextBasedProtocolMessage. The functionality that is added for this class is the SIP first line concept. A SIP
268
  /// response has the following first line: <i>200 OK SIP/2.0</i> Since it's not an "ordinary" header field, it
269
  /// requires a special treatment and gets a class of it's own: SipResponseFirstLine. In most cases a SIP response
270
  /// will be contained in a single packet but for cases it is not, only the first packet will be identified as SIP
271
  /// response layer. You can find out whether the header is complete by using SipLayer#isHeaderComplete()
272
  class SipResponseLayer : public SipLayer
273
  {
274
    friend class SipResponseFirstLine;
275
276
  public:
277
    /// Enum for SIP response status codes. List is taken from Wikipedia:
278
    /// https://en.wikipedia.org/wiki/List_of_SIP_response_codes
279
    enum SipResponseStatusCode
280
    {
281
      /// Extended search being performed may take a significant time so a forking proxy must send a 100 Trying
282
      /// response
283
      Sip100Trying,
284
      /// Destination user agent received INVITE, and is alerting user of call
285
      Sip180Ringing,
286
      /// Servers can optionally send this response to indicate a call is being forwarded
287
      Sip181CallisBeingForwarded,
288
      /// Indicates that the destination was temporarily unavailable, so the server has queued the call until the
289
      /// destination is available. A server may send multiple 182 responses to update progress of the queue
290
      Sip182Queued,
291
      /// This response may be used to send extra information for a call which is still being set up
292
      Sip183SessioninProgress,
293
      /// Can be used by User Agent Server to indicate to upstream SIP entities (including the User Agent Client
294
      /// (UAC)) that an early dialog has been terminated
295
      Sip199EarlyDialogTerminated,
296
      /// Indicates the request was successful
297
      Sip200OK,
298
      /// Indicates that the request has been accepted for processing, but the processing has not been completed
299
      Sip202Accepted,
300
      /// Indicates the request was successful, but the corresponding response will not be received
301
      Sip204NoNotification,
302
      /// The address resolved to one of several options for the user or client to choose between, which are
303
      /// listed in the message body or the message's Contact fields
304
      Sip300MultipleChoices,
305
      /// The original Request-URI is no longer valid, the new address is given in the Contact header field, and
306
      /// the client should update any records of the original Request-URI with the new value
307
      Sip301MovedPermanently,
308
      /// The client should try at the address in the Contact field. If an Expires field is present, the client
309
      /// may cache the result for that period of time
310
      Sip302MovedTemporarily,
311
      /// The Contact field details a proxy that must be used to access the requested destination
312
      Sip305UseProxy,
313
      /// The call failed, but alternatives are detailed in the message body
314
      Sip380AlternativeService,
315
      /// The request could not be understood due to malformed syntax
316
      Sip400BadRequest,
317
      /// The request requires user authentication. This response is issued by UASs and registrars
318
      Sip401Unauthorized,
319
      /// Reserved for future use
320
      Sip402PaymentRequired,
321
      /// The server understood the request, but is refusing to fulfill it
322
      Sip403Forbidden,
323
      /// The server has definitive information that the user does not exist at the domain specified in the
324
      /// Request-URI. This status is also returned if the domain in the Request-URI does not match any of the
325
      /// domains handled by the recipient of the request
326
      Sip404NotFound,
327
      /// The method specified in the Request-Line is understood, but not allowed for the address identified by
328
      /// the Request-URI
329
      Sip405MethodNotAllowed,
330
      /// The resource identified by the request is only capable of generating response entities that have content
331
      /// characteristics but not acceptable according to the Accept header field sent in the request
332
      Sip406NotAcceptable,
333
      /// The request requires user authentication. This response is issued by proxies
334
      Sip407ProxyAuthenticationRequired,
335
      /// Couldn't find the user in time. The server could not produce a response within a suitable amount of
336
      /// time, for example, if it could not determine the location of the user in time. The client MAY repeat the
337
      /// request without modifications at any later time
338
      Sip408RequestTimeout,
339
      /// User already registered
340
      Sip409Conflict,
341
      /// The user existed once, but is not available here any more
342
      Sip410Gone,
343
      /// The server will not accept the request without a valid Content-Length
344
      Sip411LengthRequired,
345
      /// The given precondition has not been met
346
      Sip412ConditionalRequestFailed,
347
      /// Request body too large
348
      Sip413RequestEntityTooLarge,
349
      /// The server is refusing to service the request because the Request-URI is longer than the server is
350
      /// willing to interpret
351
      Sip414RequestURITooLong,
352
      /// Request body in a format not supported
353
      Sip415UnsupportedMediaType,
354
      /// Request-URI is unknown to the server
355
      Sip416UnsupportedURIScheme,
356
      /// There was a resource-priority option tag, but no Resource-Priority header
357
      Sip417UnknownResourcePriority,
358
      /// Bad SIP Protocol Extension used, not understood by the server
359
      Sip420BadExtension,
360
      /// The server needs a specific extension not listed in the Supported header
361
      Sip421ExtensionRequired,
362
      /// The received request contains a Session-Expires header field with a duration below the minimum timer
363
      Sip422SessionIntervalTooSmall,
364
      /// Expiration time of the resource is too short
365
      Sip423IntervalTooBrief,
366
      /// The request's location content was malformed or otherwise unsatisfactory
367
      Sip424BadLocationInformation,
368
      /// The server rejected a non-interactive emergency call, indicating that the request was malformed enough
369
      /// that no reasonable emergency response to the alert can be determined
370
      Sip425BadAlertMessage,
371
      /// The server policy requires an Identity header, and one has not been provided
372
      Sip428UseIdentityHeader,
373
      /// The server did not receive a valid Referred-By token on the request
374
      Sip429ProvideReferrerIdentity,
375
      /// A specific flow to a user agent has failed, although other flows may succeed. This response is intended
376
      /// for use between proxy devices, and should not be seen by an endpoint (and if it is seen by one, should
377
      /// be treated as a 400 Bad Request response)
378
      Sip430FlowFailed,
379
      /// The request has been rejected because it was anonymous
380
      Sip433AnonymityDisallowed,
381
      /// The request has an Identity-Info header, and the URI scheme in that header cannot be dereferenced
382
      Sip436BadIdentityInfo,
383
      /// The server was unable to validate a certificate for the domain that signed the request
384
      Sip437UnsupportedCertificate,
385
      /// The server obtained a valid certificate that the request claimed was used to sign the request, but was
386
      /// unable to verify that signature
387
      Sip438InvalidIdentityHeader,
388
      /// The first outbound proxy the user is attempting to register through does not support the "outbound"
389
      /// feature of RFC 5626, although the registrar does
390
      Sip439FirstHopLacksOutboundSupport,
391
      /// If a SIP proxy determines a response context has insufficient Incoming Max-Breadth to carry out a
392
      /// desired parallel fork, and the proxy is unwilling/unable to compensate by forking serially or sending a
393
      /// redirect, that proxy MUST return a 440 response. A client receiving a 440 response can infer that its
394
      /// request did not reach all possible destinations
395
      Sip440MaxBreadthExceeded,
396
      /// If a SIP UA receives an INFO request associated with an Info Package that the UA has not indicated
397
      /// willingness to receive, the UA MUST send a 469 response, which contains a Recv-Info header field with
398
      /// Info Packages for which the UA is willing to receive INFO requests
399
      Sip469BadInfoPackage,
400
      /// The source of the request did not have the permission of the recipient to make such a request
401
      Sip470ConsentNeeded,
402
      /// Callee currently unavailable
403
      Sip480TemporarilyUnavailable,
404
      /// Server received a request that does not match any dialog or transaction
405
      Sip481Call_TransactionDoesNotExist,
406
      /// Server has detected a loop
407
      Sip482LoopDetected,
408
      /// Max-Forwards header has reached the value '0'
409
      Sip483TooManyHops,
410
      /// Request-URI incomplete
411
      Sip484AddressIncomplete,
412
      /// Request-URI is ambiguous
413
      Sip485Ambiguous,
414
      /// Callee is busy
415
      Sip486BusyHere,
416
      /// Request has terminated by bye or cancel
417
      Sip487RequestTerminated,
418
      /// Some aspect of the session description or the Request-URI is not acceptable
419
      Sip488NotAcceptableHere,
420
      /// The server did not understand an event package specified in an Event header field
421
      Sip489BadEvent,
422
      /// Server has some pending request from the same dialog
423
      Sip491RequestPending,
424
      /// Request contains an encrypted MIME body, which recipient can not decrypt
425
      Sip493Undecipherable,
426
      /// The server has received a request that requires a negotiated security mechanism, and the response
427
      /// contains a list of suitable security mechanisms for the requester to choose between, or a digest
428
      /// authentication challenge
429
      Sip494SecurityAgreementRequired,
430
      /// The server could not fulfill the request due to some unexpected condition
431
      Sip500ServerInternalError,
432
      /// The server does not have the ability to fulfill the request, such as because it does not recognize the
433
      /// request method. (Compare with 405 Method Not Allowed, where the server recognizes the method but does
434
      /// not allow or support it.)
435
      Sip501NotImplemented,
436
      /// The server is acting as a gateway or proxy, and received an invalid response from a downstream server
437
      /// while attempting to fulfill the request
438
      Sip502BadGateway,
439
      /// The server is undergoing maintenance or is temporarily overloaded and so cannot process the request. A
440
      /// "Retry-After" header field may specify when the client may reattempt its request
441
      Sip503ServiceUnavailable,
442
      /// The server attempted to access another server in attempting to process the request, and did not receive
443
      /// a prompt response
444
      Sip504ServerTimeout,
445
      /// The SIP protocol version in the request is not supported by the server
446
      Sip505VersionNotSupported,
447
      /// The request message length is longer than the server can process
448
      Sip513MessageTooLarge,
449
      /// The server does not support the push notification service identified in a 'pn-provider' SIP URI
450
      /// parameter
451
      Sip555PushNotificationServiceNotSupported,
452
      /// The server is unable or unwilling to meet some constraints specified in the offer
453
      Sip580PreconditionFailure,
454
      /// All possible destinations are busy. Unlike the 486 response, this response indicates the destination
455
      /// knows there are no alternative destinations (such as a voicemail server) able to accept the call
456
      Sip600BusyEverywhere,
457
      /// The destination does not wish to participate in the call, or cannot do so, and additionally the
458
      /// destination knows there are no alternative destinations (such as a voicemail server) willing to accept
459
      /// the call
460
      Sip603Decline,
461
      /// The server has authoritative information that the requested user does not exist anywhere
462
      Sip604DoesNotExistAnywhere,
463
      /// The user's agent was contacted successfully but some aspects of the session description such as the
464
      /// requested media, bandwidth, or addressing style were not acceptable
465
      Sip606NotAcceptable,
466
      /// The called party did not want this call from the calling party. Future attempts from the calling party
467
      /// are likely to be similarly rejected
468
      Sip607Unwanted,
469
      /// An intermediary machine or process rejected the call attempt
470
      Sip608Rejected,
471
      /// Unknown SIP status code
472
      SipStatusCodeUnknown
473
    };
474
475
    /// A constructor that creates the layer from an existing packet raw data
476
    /// @param[in] data A pointer to the raw data
477
    /// @param[in] dataLen Size of the data in bytes
478
    /// @param[in] prevLayer A pointer to the previous layer
479
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
480
    SipResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
481
482
    /// A constructor that allocates a new SIP response with only the first line filled. The request will be created
483
    /// without further fields. The user can then add fields using addField() or insertField() methods
484
    /// @param[in] statusCode SIP status code to set
485
    /// @param[in] statusCodeString Most status codes have their default string, e.g 200 is usually "OK" etc.
486
    /// But the user can set a non-default status code string and it will be written in the header first line. Empty
487
    /// string ("") means using the default status code string. Also, the default is using the default status code
488
    /// string
489
    /// @param[in] sipVersion SIP version to set, default is SIP/2.0
490
    explicit SipResponseLayer(SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString = "",
491
                              const std::string& sipVersion = "SIP/2.0");
492
493
    ~SipResponseLayer() override;
494
495
    /// A copy constructor for this layer. This copy constructor inherits base copy constructor SipLayer and adds
496
    /// the functionality of copying the first line as well
497
    /// @param[in] other The instance to copy from
498
    SipResponseLayer(const SipResponseLayer& other);
499
500
    /// An assignment operator overload for this layer. This method inherits base assignment operator
501
    /// SipLayer#operator=() and adds the functionality of copying the first line as well
502
    /// @param[in] other The instance to copy from
503
    SipResponseLayer& operator=(const SipResponseLayer& other);
504
505
    /// @return A pointer to the first line instance for this message
506
    SipResponseFirstLine* getFirstLine() const
507
0
    {
508
0
      return m_FirstLine;
509
0
    }
510
511
    // implement Layer's abstract methods
512
513
    std::string toString() const override;
514
515
  private:
516
    SipResponseFirstLine* m_FirstLine;
517
  };
518
519
  /// @class SipRequestFirstLine
520
  /// Represents an SIP request first line. The first line includes 3 parameters: SIP method (e.g INVITE, ACK, BYE,
521
  /// etc.), URI (e.g sip:bla@bla.com:12345) and SIP version (usually SIP/2.0). All these parameters are included in
522
  /// this class, and the user can retrieve or set them. This class cannot be instantiated by users, it's created
523
  /// inside SipRequestLayer and user can get a pointer to an instance of it. All "getters" of this class retrieve the
524
  /// actual data of the SIP request and the "setters" actually change the packet data. Since SIP is a textual
525
  /// protocol, most fields aren't of fixed size and this also applies to the first line parameters. So many "setter"
526
  /// methods of this class may need to shorten or extend the data in SipRequestLayer. These methods will return a
527
  /// false value if this action failed
528
  class SipRequestFirstLine
529
  {
530
    friend class SipRequestLayer;
531
532
  public:
533
    /// A structure containing parsed components from a SIP request first line.
534
    /// All string fields are empty if parsing fails
535
    struct SipFirstLineData
536
    {
537
      std::string method;   ///< The SIP method (e.g., INVITE, REGISTER, BYE)
538
      std::string uri;      ///< The Request-URI destination
539
      std::string version;  ///< The SIP protocol version (e.g., SIP/2.0)
540
    };
541
542
    /// @return The SIP request method
543
    SipRequestLayer::SipMethod getMethod() const
544
0
    {
545
0
      return m_Method;
546
0
    }
547
548
    /// Set the SIP request method
549
    /// @param[in] newMethod The method to set
550
    /// @return False if newMethod is SipRequestLayer#SipMethodUnknown or if shortening/extending the
551
    /// SipRequestLayer data failed. True otherwise
552
    bool setMethod(SipRequestLayer::SipMethod newMethod);
553
554
    /// @return A copied version of the URI (notice changing the return value won't change the actual data of the
555
    /// packet)
556
    std::string getUri() const;
557
558
    /// Set the URI
559
    /// @param[in] newUri The URI to set
560
    /// @return False if shortening/extending the SipRequestLayer data failed. True otherwise
561
    bool setUri(const std::string& newUri);
562
563
    /// @return The SIP version
564
    std::string getVersion() const
565
0
    {
566
0
      return m_Version;
567
0
    }
568
569
    /// A static method for parsing the SIP method out of raw data
570
    /// @param[in] data The raw data
571
    /// @param[in] dataLen The raw data length
572
    /// @return The parsed SIP method
573
    static SipRequestLayer::SipMethod parseMethod(const char* data, size_t dataLen);
574
575
    /// A static method for parsing the complete SIP request first line from raw data
576
    /// @param[in] data The raw data containing the SIP request line
577
    /// @param[in] dataLen The raw data length
578
    /// @return A pair where first indicates success/failure, and second contains the parsed data.
579
    /// If parsing fails, first is false and second contains empty strings
580
    static std::pair<bool, SipFirstLineData> parseFirstLine(const char* data, size_t dataLen);
581
582
    /// @return The size in bytes of the SIP request first line
583
    int getSize() const
584
12.2k
    {
585
12.2k
      return m_FirstLineEndOffset;
586
12.2k
    }
587
588
    /// As explained in SipRequestLayer, a SIP message can sometimes spread over more than 1 packet, so when looking
589
    /// at a single packet the header can be partial. Same goes for the first line - it can spread over more than 1
590
    /// packet. This method returns an indication whether the first line is partial
591
    /// @return False if the first line is partial, true if it's complete
592
    bool isComplete() const
593
0
    {
594
0
      return m_IsComplete;
595
0
    }
596
597
    /// @class SipRequestFirstLineException
598
    /// This exception can be thrown while constructing SipRequestFirstLine (the constructor is private, so the
599
    /// construction happens only in SipRequestLayer). This kind of exception is thrown if trying to construct with
600
    /// SIP method of SipRequestLayer#SipMethodUnknown or with empty SIP version
601
    class SipRequestFirstLineException : public std::exception
602
    {
603
    public:
604
      ~SipRequestFirstLineException() noexcept
605
8.95k
      {}
606
      void setMessage(const std::string& message)
607
0
      {
608
0
        m_Message = message;
609
0
      }
610
      virtual const char* what() const noexcept
611
0
      {
612
0
        return m_Message.c_str();
613
0
      }
614
615
    private:
616
      std::string m_Message;
617
    };
618
619
  private:
620
    SipRequestFirstLine(SipRequestLayer* sipRequest);
621
    SipRequestFirstLine(SipRequestLayer* sipRequest, SipRequestLayer::SipMethod method, const std::string& version,
622
                        const std::string& uri);
623
624
    void parseVersion();
625
626
    SipRequestLayer* m_SipRequest;
627
    SipRequestLayer::SipMethod m_Method;
628
    std::string m_Version;
629
    int m_VersionOffset;
630
    int m_UriOffset;
631
    int m_FirstLineEndOffset;
632
    bool m_IsComplete;
633
    SipRequestFirstLineException m_Exception;
634
  };
635
636
  /// @class SipResponseFirstLine
637
  /// Represents an SIP response message first line. The first line includes 2 parameters: status code (e.g 100 Trying
638
  /// ,200 OK, etc.), and SIP version (usually SIP/2.0). These 2 parameters are included in this class, and the user
639
  /// can retrieve or set them. This class cannot be instantiated by users, it's created inside SipResponseLayer and
640
  /// user can get a pointer to an instance of it. The "getter" methods of this class will retrieve the actual data of
641
  /// the SIP response and the "setter" methods will change the packet data. Since SIP is a textual protocol, most
642
  /// fields aren't of fixed size and this also applies to the first line parameters. So most "setter" methods of this
643
  /// class may need to shorten or extend the data in SipResponseLayer. These methods will return a false value if
644
  /// this action failed
645
  class SipResponseFirstLine
646
  {
647
    friend class SipResponseLayer;
648
649
  public:
650
    /// @brief A structure containing parsed components from a SIP response first line.
651
    struct FirstLineData
652
    {
653
      /// @brief The SIP protocol version (e.g., SIP/2.0)
654
      std::string version;
655
      /// @brief The response status code number (e.g., 200, 100)
656
      SipResponseLayer::SipResponseStatusCode statusCode;
657
    };
658
659
    /// @return The status code as SipResponseLayer#SipResponseStatusCode enum
660
    SipResponseLayer::SipResponseStatusCode getStatusCode() const
661
0
    {
662
0
      return m_StatusCode;
663
0
    }
664
665
    /// @return The status code number as integer (e.g 200, 100, etc.)
666
    int getStatusCodeAsInt() const;
667
668
    /// @return The status code message (e.g "OK", "Trying", etc.)
669
    std::string getStatusCodeString() const;
670
671
    /// Set the status code
672
    /// @param[in] newStatusCode The new status code to set
673
    /// @param[in] statusCodeString An optional parameter: set a non-default status code message (e.g "Bla Bla"
674
    /// instead of "Not Found"). If this parameter isn't supplied or supplied as empty string (""), the default
675
    /// message for the status code will be set
676
    bool setStatusCode(SipResponseLayer::SipResponseStatusCode newStatusCode, std::string statusCodeString = "");
677
678
    /// @return The SIP version
679
    std::string getVersion() const
680
0
    {
681
0
      return m_Version;
682
0
    }
683
684
    /// Set the SIP version. The version to set is expected to be in the format of SIP/x.y otherwise an error will
685
    /// be written to log
686
    /// @param[in] newVersion The SIP version to set
687
    void setVersion(const std::string& newVersion);
688
689
    /// A static method for parsing the SIP status code out of raw data
690
    /// @param[in] data The raw data
691
    /// @param[in] dataLen The raw data length
692
    /// @return The parsed SIP status code as enum
693
    static SipResponseLayer::SipResponseStatusCode parseStatusCode(const char* data, size_t dataLen);
694
695
    /// A static method for parsing the SIP version out of raw data
696
    /// @param[in] data The raw data
697
    /// @param[in] dataLen The raw data length
698
    /// @return The parsed SIP version string or an empty string if version cannot be extracted
699
    static std::string parseVersion(const char* data, size_t dataLen);
700
701
    /// @brief A static method for parsing the complete SIP response first line from raw data
702
    /// @param data The raw data containing the SIP response line
703
    /// @param dataLen The raw data length
704
    /// @return A pair where first indicates success/failure, and second contains the parsed data.
705
    static std::pair<bool, FirstLineData> parseFirstLine(const char* data, size_t dataLen);
706
707
    /// @return The size in bytes of the SIP response first line
708
    int getSize() const
709
20.5k
    {
710
20.5k
      return m_FirstLineEndOffset;
711
20.5k
    }
712
713
    /// As explained in SipResponseLayer, A SIP message can sometimes spread over more than 1 packet, so when
714
    /// looking at a single packet the header can be partial. Same goes for the first line - it can spread over more
715
    /// than 1 packet. This method returns an indication whether the first line is partial
716
    /// @return False if the first line is partial, true if it's complete
717
    bool isComplete() const
718
0
    {
719
0
      return m_IsComplete;
720
0
    }
721
722
    /// @class SipResponseFirstLineException
723
    /// This exception can be thrown while constructing SipResponseFirstLine (the constructor is private, so the
724
    /// construction happens only in SipResponseLayer). This kind of exception will be thrown if trying to construct
725
    /// with SIP status code of SipResponseLayer#SipStatusCodeUnknown or with an empty SIP version
726
    class SipResponseFirstLineException : public std::exception
727
    {
728
    public:
729
      ~SipResponseFirstLineException() noexcept
730
15.9k
      {}
731
      void setMessage(const std::string& message)
732
0
      {
733
0
        m_Message = message;
734
0
      }
735
      virtual const char* what() const noexcept
736
0
      {
737
0
        return m_Message.c_str();
738
0
      }
739
740
    private:
741
      std::string m_Message;
742
    };
743
744
  private:
745
    SipResponseFirstLine(SipResponseLayer* sipResponse);
746
    SipResponseFirstLine(SipResponseLayer* sipResponse, const std::string& version,
747
                         SipResponseLayer::SipResponseStatusCode statusCode, std::string statusCodeString = "");
748
749
    SipResponseLayer* m_SipResponse;
750
    std::string m_Version;
751
    SipResponseLayer::SipResponseStatusCode m_StatusCode;
752
    int m_FirstLineEndOffset;
753
    bool m_IsComplete;
754
    SipResponseFirstLineException m_Exception;
755
  };
756
757
}  // namespace pcpp