1. Си / Говнокод #25188

    +3

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    enum {
        HOST = INADDR_LOOPBACK,
        PORT = 6666,
        MAX_BUF = 1024
    };
    
    struct sock {
        int sockfd;
        int addrlen;
        struct sockaddr_in addr;
    } host, client;
    
    int check(int x, char*msg)
    {
        if (!~x) {
            perror(msg);
            exit(1);
        }
        return x;
    }
    
    #define QUOTE_(...) #__VA_ARGS__
    #define QUOTE(...) QUOTE_(__VA_ARGS__)
    #define CHECK(...) check(__VA_ARGS__, QUOTE(line __LINE__: __VA_ARGS__))
    
    int main(int argc, char**argv)
    {
        struct sock host, client;
        host.sockfd = CHECK(socket(AF_INET, SOCK_STREAM, 0)),
        host.addr = (struct sockaddr_in){AF_INET, htons(PORT), htonl(HOST)};
        CHECK(bind(host.sockfd, &(struct sockaddr)host.addr, sizeof(host.addr)));
        CHECK(listen(host.sockfd, 1));
        CHECK(client.sockfd = accept(host.sockfd, (void*)&client.addr, &client.addrlen));
        printf("connected: %s\n", inet_ntoa(client.addr.sin_addr));
        
        struct {int len; char buf[MAX_BUF];} msg;
        while (CHECK(msg.len = recv(client.sockfd, msg.buf, MAX_BUF - 1, 0)) && msg.len) {
            msg.buf[msg.len] = 0;
            printf("%s", msg.buf);
            send(client.sockfd, msg.buf, msg.len, 0);
        }
        close(client.sockfd);
        close(host.sockfd);
        return 0;
    }

    Почему если закоментить 36-ю строчку адрес килента 0.0.0.0?

    Запостил: 666_N33D135, 13 Декабря 2018

    Комментарии (63) RSS

    • #c #socket
      Ответить
    • Потому что
      struct sock {
          int sockfd;
          int addrlen;
          struct sockaddr_in addr;
      } host, client;
      Ответить
      • struct cock {
            int comb;
            int wattles;
            int beak;
            struct feather plumage;
        } rooster, chicken;
        Ответить
      • там на самом деле sockaddr а не sockaddr_in
        потому что сокет может быть и не интернетный

        В нем char sa_data[14];

        А вот в sockaddr_in эти 14 байт расложили так

        u_short sin_port; // на порт 2 байта
        struct in_addr sin_addr; //на адрес 4 байта
        char sin_zero[8]; // на добивку
        Ответить
        • Тут вот что интересно: в глобальном контексте (что обычно кладётся в секцию .data) объявлено:
          struct sock {
              int sockfd;
              int addrlen;
              struct sockaddr_in addr;
          } host, client;


          Переменные с теми же самыми именами объявлены в функции main (что обычно кладётся в стек):
          struct sock host, client;


          Если убрать локальное объявление (ту самую 36-ю строку), то у нидлеса программа не может определить адрес клиента.
          Ответить
          • > не может определить адрес клиента
            И правильно делает! Ибо accept'у некуда писать этот адрес. В отличие от случая с локалкой где всё случайно работает из-за UB'а.
            Ответить
            • а это UB что
              если int pitux
              то ptiux == 0?
              Ответить
              • > int putix; // pitux == 0
                В глобалке - ноль. В локалке - UB.

                Инициализируйте переменные, блеать.

                The addrlen argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.
                Ответить
                • тогда нидлесу просто подвезло.

                  А мог бы и случиться адрес 169.253.0.0
                  Ответить
                  • Всё хуже. 169.253.UB.UB.

                    The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.
                    Ответить
                    • а неужели шланг какойнито не ругается что ты шадоуишь локальной пирименной глабально-статисськую?
                      Ответить
                      • Нет.

                        Х.з., сомнительный ворнинг так то (у студии есть, емнип). Из хедеров много всякого говна торчит, которое ты никогда юзать не будешь. Запрещать все эти имена юзать в качестве локалок, имхо, оверкилл.

                        А вот в пределах одной функции можно было бы и показать, это на 99% залёт. Но тоже нет.
                        Ответить
                • Сапасибо, я видимо этот пукт упустил из виду.
                  client.addrlen = sizeof(struct sockaddr);
                  Теперь всё работает.
                  Ответить
            • >> В отличие от случая с локалкой

              В обоих случаях результат зависит от деталей реализации.

              1. Глобальные переменные.

              У секции .data есть инициализированная часть, лежащая в экзешнике, которая забита нулями и прочими начальными значениями (константами и правой частью от выражений типа int x = 42;), и неинициализированная часть, которая создаётся после загрузки (чем она забита, решает загрузчик экзешника).

              Кудкудкудкуда ляжет переменная (в инициализированную часть или в неинициализированную), решают кококонпелятор и линкер.

              2. Локальные переменные.

              Тут всё хуже. В стек может наложить кто угодно. Я помню, как Царь полагался на то, что его промежуточные данные в стеке между вызовами функций сохраняются, и его программа обломалась в Ideone, где какой-то патч типа Propolice или Stackguard чистит стек вилкой.

              Значит, у Нидлеса вариант с локальными переменными работает только потому, что по счастливой случайности кто-то в эту область стека положил ненулевые значения.
              Ответить
              • > от деталей реализации
                6.7.8 Initialization
                10) <...> If an object that has static storage duration is not initialized explicitly, then <...> if it has arithmetic type, it is initialized to (positive or unsigned) zero.
                Ответить
                • То есть если операционка не справляется, то рантайм должен занулить как минимум все переменные загадочного арифметического типа?

                  А с «неарифметическими» что будет?
                  Ответить
                  • - if it has pointer type, it is initialized to a null pointer;
                    - if it has arithmetic type, it is initialized to (positive or unsigned) zero;
                    - if it is an aggregate, every member is initialized (recursively) according to these rules;
                    - if it is a union, the first named member is initialized (recursively) according to these rules.


                    Но это всё касается только static storage duration, на локалки это правило не действует и они наполнены UB'ами.
                    Ответить
                    • >> if it is a union, the first named member is initialized

                      А если другие члены окажутся длиннее первого, то их хвосты можно не инициализировать?
                      Ответить
                      • Очевидно да. Паддинги тоже можно не инициализировать. Само собой, на практике всё это будет забито нулями, просто без гарантий.

                        > если операционка не справляется
                        Ну это странно... У процесса своей грязной памяти ещё нету, а брать её из других процессов не почистив вилкой - ебучее решето. Хотя какая-нибудь мелкая ось, у которой всё доверенное и без изоляции, в принципе, имеет право.
                        Ответить
                    • Надо проверить, противоречат ли друг другу эти условия в безумном случае, когда эти все типы являются членами union'а.

                      1. В некоторых системах null pointer может быть не нулём, а специально подготовленным значением (-1 или что-нибудь ещё хуже). Или в сишке теперь он всегда ноль?

                      2. Плавающий питух имеет множество реализаций.

                      С IEEE754 повезло: там порядок хранится со смещением (bias), так что порядок, забитый нулями соответствует самому низкому значению порядка (-127 во float, -1023 в double, -16383 в long double), поэтому если забить нулями и мантиссу, и порядок, как раз получим +0, что не противоречит стандарту.

                      Если питух другой, то для обнуления числа придётся забивать чем-нибудь другим.

                      Стандарт позволяет использовать не 754?
                      Ответить
                      • > когда эти все типы являются членами union'а
                        Накладок не будет т.к. только первый именованный из них обязан инициализироваться.

                        > использовать не 754
                        Походу да:
                        Annex F (normative) IEC 60559 floating-point arithmetic
                        F.1 Introduction
                        1) <...> An implementation that defines __STDC_IEC_559__ shall conform to the specifications in this annex.

                        Другим плавучим питухам просто нельзя объявлять это макро.
                        Ответить
                        • Проверю каких-нибудь случайных плавучих питухов:

                          1. MBF (Microsoft binary format, BASIC). Порядок со смещением 128, причём для чисел любой точности. Старший разряд мантиссы подразумевается равным единице, как у IEEE 754. Denormal, inf, NaN не поддерживаются. Знаковый бит где-то в середине.

                          Если все биты обнулены, то хранится +0.

                          2. HFP (hexadecimal floating point, IBM System/360). Порядок со смещением 64 для чисел любой точности, основание порядка равно 16, а не 2 (поэтому старший подразумеваемый разряд мантиссы равен нулю). Знаковый бит в самом начале.

                          Если все биты обнулены, то хранится +0.

                          3. 48-битный Real в Трубопаскакале. Порядок хранится со смещением 128. Мантисса записывается задом наперёд. Знаковый бит где-то в середине. Старший разряд мантиссы подразумевается равным единице, как у IEEE 754.

                          Если все биты обнулены, то хранится +0.

                          Найденные питухи легко обнулять. Нужно найти какой-нибудь посложнее.

                          У БЭСМ порядок хранился без смещения (т. е. нули соответствовали не минимальному значению, а нулевой степени). Но я пока ещё не прочитал, как там хранилась мантисса.
                          Ответить
                          • У БЭСМ-6 порядок уже со смещением 64, т. е. для малых чисел в машинном представлении нули. А вот мантисса хранилась в дополнительном коде (two's complement), как у целого питуха. Подразумеваемой единицы не было.

                            В любом случае, у БЭСМ-6 ноль состоял из нулей. А вот минус нуля (судя по дополнительному коду) не было.

                            P.S. Какие ещё плавпитухи бывают?

                            P.P.S. В IEEE 754 описан ещё двоично-десятичный питух. Строится он по тому же принципу, что и двоичный, только бывают ненормальные кодировки порядка для экономии битов.
                            Ответить
                    • Почему кстати так?

                      Потму што память под static выделяется в момент запуска и там всё равно чем ее забить, а стек выделяется каждый раз и нехуй тратить время на ненужное?
                      Ответить
                  • > загадочного арифметического типа
                    6.2.5 Types
                    18) Integer and floating types are collectively called arithmetic types.
                    Ответить
                  • > операционка не справляется
                    Или её ещё нет или вообще нет, а сишку юзать хочется...
                    Ответить
                    • Помню прошивку, написанную на С++. Она генерировалась из exe-шника, но точка входа устанавливалась на main. Поэтому конструкторы для глобальных объектов не вызывались... и ненулевой инициализации глобальных/статических переменных, кажется, тоже не было.
                      Ответить
            • > accept'у некуда писать этот адрес
              всмысле некуда? Я же передал укозатель на сьруктуру?
              Ответить
              • RTFM.Я выше привел цитату из мана почему твой код не работает.

                З.Ы. Первый вопрос, который должен возникать при вызове незнакомой функции - "а где она берёт длину буфера?"
                Ответить
                • > а где она берёт длину буфера?
                  Я думал она знает размер сьруктуры.
                  Ответить
                  • Размер структуры она знает. А вот размер твоего буфера - нет. Ну и подразумеваемого размера нету т.к. адреса могут быть разного типа.

                    А вслепую вроде только gets (функция, запрещённая к использованию) пишет.
                    Ответить
                    • > только gets
                      ещё scanf("%s", &s)
                      Ответить
                      • В "PHP" функции fgets, fread, fscanf сами выделяют память. Именно поэтому я за "PHP".
                        Ответить
        • > там на самом деле sockaddr а не sockaddr_in
          sockaddr_in чтоб кастовать меньше
          Ответить
    • Продолжим ревью.

      > if (!~x)
      Это что за покемон? Хакерская проверка на -1? Ещё не хватало у знакового числа биты инвертировать...

      > && msg.len
      Недостижимый код, у тебя CHECK и так вернёт 0. while (msg.len = CHECK(recv(...)), имхо, было бы немного наглядней.

      > send(client.sockfd, msg.buf, msg.len, 0);
      Уверен, что улетит одним куском? Так можно и байты проебать. Ну и CHECK забыл.
      Ответить
      • да тут много хуни
        начиная с непроверки результтата байнда (всякый адрес олреди ин юз) и заканчивая вообще говоря тем что TCP (STREAM) у нас с recv, что тоже как-то подозрительно (аут оф баунд чилишоли?)

        Короче Стивенсом пиздить по голове кмк
        Ответить
        • Х.з., я тоже всегда recv() вместо read() юзаю чтобы подчеркнуть, что это именно сокет.

          > непроверки результтата байнда
          Проверен же. Или я слепой?
          Ответить
        • > Стивенсом
          Островом сокровищ? Или криптономиконом? Второе, наверное, ближе к теме.
          Ответить
        • > непроверки результтата байнда
          Я всё проверил.

          > TCP (STREAM) у нас с recv
          прочитал на rsdn что read/write не рекомендуется

          > аут оф баунд
          это с чего вдрукк?
          Ответить
          • recv обычно используется либо для udp либо для OOB. Но это в linux, а windows может быть и иначе

            man tcp
            Ответить
            • В windows read/write нельзя. А для UDP в линукс sendto и recvfrom.
              Ответить
              • recv(sockfd, buf, len, flags) это эквивалент recvfrom(sockfd, buf, len, flags, NULL, NULL);

                А так же прямо из мана
                The only difference between recv() and read(2) is the presence of flags.

                А про винду ты прав: read на выньсок не раб
                Ответить
      • > покемон?
        но он такой маленький няшка, как его не вставить? ^_^

        > Ещё не хватало у знакового числа биты инвертировать
        а знаковость как на побитловые опереции влияет?

        > Недостижимый код
        изначально были проверки на -1 без функции, там нужно было, так и осталось.

        > улетит одним куском?
        на холокосте да, тестовая программка же.
        Ответить
        • > знаковость
          Стандарта под рукой нет, но у меня ощущение, что там возможен UB.
          Ответить
          • 4 The result of the ~ operator is the bitwise complement of its (promoted) operand (that is,
            each bit in the result is set if and only if the corresponding bit in the converted operand is
            not set). The integer promotions are performed on the operand, and the result has the
            promoted type. If the promoted type is an unsigned type, the expression ~E is equivalent
            to the maximum value representable in that type minus E.


            Обычная побитовая инверсия.
            Ответить
            • C11 6.5.4 [...] These operators yield values that depend on the internal representations of integers, and have implementation-defined and undefined aspects for signed types.

              Педанты могут пройтись по упоминаниям sign bit, trap (representation), negative zero и какие там ещё ужасы. Но !~x того не стоит.
              Ответить
              • Не стоит знать как работает ~?
                Ответить
                • Стоит знать, как ~ работает с unsigned, и знать, что для signed стандарт того же не гарантирует.

                  А не стоит - без причин добавлять завязку на особенности реализации.

                  В мане ведь было: "On error, -1 is returned", а не ~0U. И ничего постыдного нет в очевидной проверке x==-1.
                  Ответить
                  • Да и вообще, козырять mad bit shufflor skillz для проверки -1 выглядит ребячеством, когда кто-то уже додумался до Fast_inverse_square_root для IEEE 754.
                    Ответить
                  • Чисто теоретически в мире существует как несколько реализаций отрицательных чисел:

                    1) Дополнительный код (two's complement), когда вся арифметика по модулю 2 в степени N, где N — количество двоичных разрядов в числе.

                    Тогда -1 = ~0, наибольшее положительное число равно pow(2, n-1) - 1; наименьшее число равно - pow(2, n-1).

                    Применяется повсеместно для целых чисел.

                    2) Обратный код (one's complement). Его особенность в том, что -x == ~x, поэтому у него два нуля: +0 и -0 (в представлении последнего все биты установлены, как у числа pow(2, n-1) - 1).

                    Диапазон допустимых значений симметричный: [ -(pow(2, n-1) - 1); + (pow(2, n-1) - 1)].

                    Операции между положительными и отрицательными числами в таком представлении вызывают головную боль. К счастью, он применялся только в древних компьютерах, и сейчас о нём можно забыть.

                    3) Прямой код. У числа есть знаковый бит, за которым следует модуль числа. Проблемы те же, что и у обратного кода: два нуля (+0 и -0), сложность сложения чисел разных знаков. Однако, есть и положительные стороны: легко умножать и делить числа произвольного знака, легко выводить на экран.

                    В прямом коде обычно хранится мантисса вещественных чисел.

                    4) Число со смещением. Ко всем числам прибавляется константа, чтобы они стали положительными. В таком формате обычно хранится порядок (показатель, экспонента) вещественных чисел. В этом формате и ноль выглядит не как ноль, а как 1 << (n - 1).

                    *****

                    По факту на большинстве современных процессоров для хранения целых используется дополнительный код (two's complement), где -1 == ~0 (вообще -x == ~(x - 1)), но если пишем портируемый код, то полагаться на это нельзя.
                    Ответить
    • Ебанутые, откройте для себя жаву уже.
      Ответить
      • >откройте для себя жабу

        Медик?
        Ответить
        • Толк от вскрытий жаб? Лучше крыс –— их много и на людей они больше похожи.
          Ответить
          • лучше людей
            живых

            а крыс жалко, если честно
            они няшечки такие
            Ответить
          • Вскрыл твоё тело и поместил туда живого кота, проверь.
            Ответить
            • Поместил вашего живого кота в ящик Шрёдингера. Ещё не проверял.
              Ответить
      • еще один фриккоторый никакого языка кроме джавы не знает< и считает что на ней можно написать все что угодно, а си ненужен?
        Ответить
      • Жаба умеет сырые укозатели? Можно без гц?

        Говно виртуольно-вербозное анально-объектно-ориентированое.
        Ответить

    Добавить комментарий