ua_debug_dump_pkgs_file.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. /**
  5. * This code is used to generate a binary file for every request type
  6. * which can be sent from a client to the server.
  7. * These files form the basic corpus for fuzzing the server.
  8. */
  9. #ifndef UA_DEBUG_DUMP_PKGS_FILE
  10. #error UA_DEBUG_DUMP_PKGS_FILE must be defined
  11. #endif
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <ua_types.h>
  15. #include <server/ua_server_internal.h>
  16. #include <unistd.h>
  17. #include "ua_transport_generated_encoding_binary.h"
  18. #include "ua_types_generated_encoding_binary.h"
  19. unsigned int UA_dump_chunkCount = 0;
  20. char *UA_dump_messageTypes[] = {"ack", "hel", "msg", "opn", "clo", "err", "unk"};
  21. struct UA_dump_filename {
  22. const char *messageType;
  23. char serviceName[100];
  24. };
  25. void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection, UA_ByteString *messageBuffer);
  26. /**
  27. * Gets a pointer to the string representing the given message type from UA_dump_messageTypes.
  28. * Used for naming the dumped file.
  29. */
  30. static const char * UA_debug_dumpGetMessageTypePrefix(UA_UInt32 messageType) {
  31. switch(messageType & 0x00ffffff) {
  32. case UA_MESSAGETYPE_ACK:
  33. return UA_dump_messageTypes[0];
  34. case UA_MESSAGETYPE_HEL:
  35. return UA_dump_messageTypes[1];
  36. case UA_MESSAGETYPE_MSG:
  37. return UA_dump_messageTypes[2];
  38. case UA_MESSAGETYPE_OPN:
  39. return UA_dump_messageTypes[3];
  40. case UA_MESSAGETYPE_CLO:
  41. return UA_dump_messageTypes[4];
  42. case UA_MESSAGETYPE_ERR:
  43. return UA_dump_messageTypes[5];
  44. default:
  45. return UA_dump_messageTypes[6];
  46. }
  47. }
  48. /**
  49. * Decode the request message type from the given byte string and
  50. * set the global requestServiceName variable to the name of the request.
  51. * E.g. `GetEndpointsRequest`
  52. */
  53. static UA_StatusCode UA_debug_dumpSetServiceName(const UA_ByteString *msg, char serviceNameTarget[100]) {
  54. /* At 0, the nodeid starts... */
  55. size_t offset = 0;
  56. /* Decode the nodeid */
  57. UA_NodeId requestTypeId;
  58. UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
  59. if(retval != UA_STATUSCODE_GOOD)
  60. return retval;
  61. if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC || requestTypeId.namespaceIndex != 0) {
  62. snprintf(serviceNameTarget, 100, "invalid_request_id");
  63. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  64. }
  65. const UA_DataType *requestType = NULL;
  66. for (size_t i=0; i<UA_TYPES_COUNT; i++) {
  67. if (UA_TYPES[i].binaryEncodingId == requestTypeId.identifier.numeric) {
  68. requestType = &UA_TYPES[i];
  69. break;
  70. }
  71. }
  72. if (requestType == NULL) {
  73. snprintf(serviceNameTarget, 100, "invalid_request_no_type");
  74. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  75. }
  76. snprintf(serviceNameTarget, 100, "_%s", requestType->typeName);
  77. return UA_STATUSCODE_GOOD;
  78. }
  79. /**
  80. * We need to decode the given binary message to get the name of the called service.
  81. * This method is used if the connection has no channel yet.
  82. */
  83. static UA_StatusCode
  84. UA_debug_dump_setName_withoutChannel(UA_Server *server, UA_Connection *connection,
  85. UA_ByteString *message, struct UA_dump_filename* dump_filename) {
  86. size_t offset = 0;
  87. UA_TcpMessageHeader tcpMessageHeader;
  88. UA_StatusCode retval =
  89. UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader);
  90. if(retval != UA_STATUSCODE_GOOD)
  91. return retval;
  92. dump_filename->messageType = UA_debug_dumpGetMessageTypePrefix(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff);
  93. if ((tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) == UA_MESSAGETYPE_MSG) {
  94. // this should not happen in normal operation
  95. UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER, "Got MSG package without channel.");
  96. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  97. }
  98. return UA_STATUSCODE_GOOD;
  99. }
  100. /**
  101. * We need to decode the given binary message to get the name of the called service.
  102. * This method is used if the connection an established secure channel.
  103. *
  104. * message is the decoded message starting at the nodeid of the content type.
  105. */
  106. static UA_StatusCode
  107. UA_debug_dump_setName_withChannel(void *application, UA_SecureChannel *channel,
  108. UA_MessageType messagetype, UA_UInt32 requestId,
  109. const UA_ByteString *message) {
  110. struct UA_dump_filename *dump_filename = (struct UA_dump_filename *)application;
  111. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  112. dump_filename->messageType = UA_debug_dumpGetMessageTypePrefix(messagetype);
  113. if (messagetype == UA_MESSAGETYPE_MSG) {
  114. UA_debug_dumpSetServiceName(message, dump_filename->serviceName);
  115. }
  116. return retval;
  117. }
  118. /**
  119. * Called in processCompleteChunk for every complete chunk which is received by the server.
  120. *
  121. * It will first try to decode the message to get the name of the called service.
  122. * When we have a name the message is dumped as binary to that file.
  123. * If the file already exists a new file will be created with a counter at the end.
  124. */
  125. void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection, UA_ByteString *messageBuffer) {
  126. struct UA_dump_filename dump_filename;
  127. dump_filename.messageType = NULL;
  128. dump_filename.serviceName[0] = 0;
  129. UA_StatusCode retval;
  130. if(!connection->channel)
  131. retval = UA_debug_dump_setName_withoutChannel(server, connection, messageBuffer, &dump_filename);
  132. else {
  133. // make a backup of the sequence number and reset it, because processChunk increases it
  134. UA_UInt32 seqBackup = connection->channel->receiveSequenceNumber;
  135. retval = UA_SecureChannel_processChunk(connection->channel, messageBuffer,
  136. UA_debug_dump_setName_withChannel,
  137. &dump_filename);
  138. connection->channel->receiveSequenceNumber = seqBackup;
  139. }
  140. char fileName[250];
  141. snprintf(fileName, 255, "%s/%05d_%s%s", UA_CORPUS_OUTPUT_DIR, ++UA_dump_chunkCount, dump_filename.messageType ? dump_filename.messageType : "", dump_filename.serviceName);
  142. char dumpOutputFile[255];
  143. snprintf(dumpOutputFile, 255, "%s.bin", fileName);
  144. {
  145. // check if file exists and if yes create a counting filename to avoid overwriting
  146. unsigned cnt = 1;
  147. while ( access( dumpOutputFile, F_OK ) != -1 ) {
  148. snprintf(dumpOutputFile, 255, "%s_%d.bin", fileName, cnt);
  149. cnt++;
  150. }
  151. }
  152. UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER, "Dumping package %s", dumpOutputFile);
  153. FILE *write_ptr;
  154. write_ptr = fopen(dumpOutputFile, "ab");
  155. fwrite(messageBuffer->data, messageBuffer->length, 1, write_ptr); // write 10 bytes from our buffer
  156. fclose(write_ptr);
  157. }