ua_debug_dump_pkgs_file.c 6.9 KB

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